标准是否有任何理由将它们指定为 templatestruct
而不是简单的 boolean constexpr
?
在另一个问题中,可能会在主要问题的一个很好的答案中得到回答,一个人将如何enable_if
处理非结构版本的东西?
标准是否有任何理由将它们指定为 templatestruct
而不是简单的 boolean constexpr
?
在另一个问题中,可能会在主要问题的一个很好的答案中得到回答,一个人将如何enable_if
处理非结构版本的东西?
一个原因是constexpr
函数不能提供嵌套type
成员,这在某些元编程情况下很有用。
为了清楚起见,我不仅在谈论产生类型并且显然不能成为函数的转换特征(如)。所有类型特征都提供了这样一个嵌套成员,甚至是一元类型特征和二元类型特征。例如是.make_unsigned
constexpr
type
is_void<int>::type
false_type
当然,这可以解决std::integral_constant<bool, the_constexpr_function_version_of_some_trait<T>()>
,但它不会那么实用。
无论如何,如果你真的想要类似函数的语法,那已经是可能的了。您可以只使用特征构造函数并利用constexpr 隐式转换integral_constant
:
static_assert(std::is_void<void>(), "void is void; who would have thunk?");
对于转换特征,您可以使用模板别名来获取接近该语法的内容:
template <bool Condition, typename T = void>
using enable_if = typename std::enable_if<Condition, T>::type;
// usage:
// template <typename T> enable_if<is_void<T>(), int> f();
//
// make_unsigned<T> x;
注意:这最终看起来更像是一个咆哮而不是一个正确的答案......虽然我确实在阅读以前的答案时感到有些痒,所以请原谅我;)
首先,类特征在历史上是用模板结构完成的,因为它们早constexpr
于decltype
. 如果没有这两个,使用函数就需要做更多的工作,尽管各种库实现is_base_of
必须在内部使用函数才能获得正确的继承。
使用函数有什么好处?
typename ::type
看起来很愚蠢 TM)实际上,继承可能是反对类特征的主要观点。您需要专门化所有派生类才能像momma那样做,这很烦人。很烦人。使用函数,您只需继承特征,并且可以根据需要进行专业化。
有什么缺点?
当然,有人可能会争辩说这实际上很烦人:specialization iterator_traits
,你经常无偿继承 fromstd::iterator_traits
只是为了获得默认值。不同的功能会很自然地提供这一点。
它可以工作吗?
constexpr
好吧,总而言之,除了 from (但是,这不是特征)之外,一切都将基于enable_if
此,您将要:
template <typename T>
typename enable_if<std::is_integral(T()) and
std::is_signed(T())>::type
注意:我没有std::declval
在这里使用,因为它需要一个未评估的上下文(即,sizeof
或decltype
大部分)。因此,一个额外的要求(不是立即可见的)T
是默认可构造的。
如果你真的想要,有一个 hack:
#define VALUE_OF(Type_) decltype(std::declval<T>())
template <typename T>
typename enable_if<std::is_integral(VALUE_OF(T)) and
std::is_signed(VALUE_OF(T))>::type
如果我需要一个类型,而不是一个常量怎么办?
decltype(common_type(std::declval<T>(), std::declval<U>()))
我也没有看到问题(是的,我在这里使用declval
)。constexpr
但是...传递类型与;无关 constexpr
函数在返回您感兴趣的值时很有用。当然可以使用返回复杂类型的函数,但它们不是constexpr
,而且您不使用类型的值。
如果我需要链接 trais 和 types 怎么办?
具有讽刺意味的是,这就是功能大放异彩的地方:)
// class version
template <typename Container>
struct iterator { typedef typename Container::iterator type; };
template <typename Container>
struct iterator<Container const> {
typedef typename Container::const_iterator type;
};
template <typename Container>
struct pointer_type {
typedef typename iterator<Container>::type::pointer_type type;
};
template <typename Container>
typename pointer_type<Container>::type front(Container& c);
// Here, have a cookie and a glass of milk for reading so far, good boy!
// Don't worry, the worse is behind you.
// function version
template <typename Container>
auto front(Container& c) -> decltype(*begin(c));
什么!骗子!没有定义特征!
嗯……其实,这就是重点。随着decltype
,大量的特征刚刚变得多余。
干!
继承才有效!
采取基本的类层次结构:
struct Base {};
struct Derived: Base {};
struct Rederived: Derived {};
并定义一个特征:
// class version
template <typename T>
struct some_trait: std::false_type {};
template <>
struct some_trait<Base>: std::true_type {};
template <>
struct some_trait<Derived>: some_trait<Base> {}; // to inherit behavior
template <>
struct some_trait<Rederived>: some_trait<Derived> {};
注意:特性 forDerived
不直接声明true
,false
而是从其祖先那里获取行为。这样,如果祖先改变立场,整个层次结构就会自动跟随。大多数时候,由于基本功能是由祖先提供的,因此遵循其特性是有意义的。类型特征更是如此。
// function version
constexpr bool some_trait(...) { return false; }
constexpr bool some_trait(Base const&) { return true; }
注意:省略号的使用是有意的,这是包罗万象的重载。模板函数将比其他重载更好地匹配(不需要转换),而省略号始终是最差匹配,以保证它只选择那些不适合其他重载的函数。
我想没有必要精确说明后一种方法有多简洁?您不仅可以摆脱template <>
混乱,还可以免费获得继承权。
可以
enable_if
这样实施吗?
不幸的是,我不这么认为,但正如我已经说过的:这不是一个特征。而且该std
版本很好地工作,constexpr
因为它使用bool
参数,而不是类型:)
那么为什么?
Well, the only technical reason is that a good portion of the code already relies on a number of traits that was historically provided as types (std::numeric_limit
) so consistency would dictate it.
Furthermore it makes migration from boost::is_*
just so easier!
I do, personally, think it is unfortunate. But I am probably much more eager to review the existing code I wrote than the average corporation.
一个原因是 type_traits 提议比 constexpr 提议更早。
另一种方法是,如果需要,您可以为自己的类型添加特化。
可能是因为 boost 已经有了一个使用模板实现的 type_traits 版本。
我们都知道标准委员会中有多少人复制提升。
我想说的主要原因是它type_traits
已经是标准的一部分,tr1
因此基本上可以保证以或多或少相同的形式出现在标准中,所以它早于constexpr
. 其他可能的原因是:
remove_pointer
)定义 atype
而不是 a value
,因此它们必须以这种方式表达。为定义值的特征和定义类型的特征提供不同的接口似乎是不必要的templated structs
可以部分特化,而函数不能,这样可能会使某些特征的实现更容易对于您的第二个问题:enable_if
定义 a type
(或不定义,如果它被传递为假) a 内的嵌套 typedefstruct
确实是要走的路