rub*_*nvb 8 c++ type-traits template-meta-programming c++11
是否有任何理由将标准指定为模板struct
而不是简单的布尔值constexpr
?
在一个额外的问题,可能会在一个很好的答案中回答主要问题,如何enable_if
处理非结构版本?
R. *_*des 18
一个原因是constexpr
函数无法提供嵌套type
成员,这在某些元编程情况下很有用.
为了说清楚,我不仅仅谈论产生类型的转换特征(例如make_unsigned
),显然不能成为constexpr
函数.所有类型特征都提供这样的嵌套type
成员,甚至是一元类型特征和二元类型特征.例如is_void<int>::type
是false_type
.
当然,这可以解决std::integral_constant<bool, the_constexpr_function_version_of_some_trait<T>()>
,但它不会那么实用.
无论如何,如果你真的想要类似函数的语法,那就已经可以了.您可以使用traits构造函数并利用constexpr隐式转换integral_constant
:
static_assert(std::is_void<void>(), "void is void; who would have thunk?");
Run Code Online (Sandbox Code Playgroud)
对于转换特征,您可以使用模板别名来获取接近该语法的内容:
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;
Run Code Online (Sandbox Code Playgroud)
注意:这最终看起来更像是咆哮而不是正确的答案...不过,读之前的答案时我确实有些痒,所以请原谅;)
首先,类特征历来是通过模板结构完成的,因为它们早于constexpr
和decltype
。如果没有这两个,使用函数的工作量会增加一些,尽管各种库实现is_base_of
必须在内部使用函数才能获得正确的继承。
使用函数有什么好处?
typename ::type
看起来很蠢 TM)实际上,继承可能是反对阶级特征的要点。您需要专门化所有派生类才能像momma那样做,这真是太烦人了。很烦人。使用函数,您只需继承特征,并且可以根据需要进行专门化。
有什么缺点?
当然,有人可能会说这实际上很烦人:专门化iterator_traits
,你经常无缘无故地继承std::iterator_traits
只是为了获得默认值。不同的函数自然会提供这一点。
能行吗?
好吧,总而言之constexpr
,除了 from enable_if
(但是,这不是一个特质)之外,一切都将基于这个词,你会:
template <typename T>
typename enable_if<std::is_integral(T()) and
std::is_signed(T())>::type
Run Code Online (Sandbox Code Playgroud)
注意:我没有std::declval
在这里使用,因为它需要一个未评估的上下文(即,sizeof
或decltype
大部分)。因此,一项附加要求(不是立即可见)是T
默认可构造的。
如果你真的想要,有一个技巧:
#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
Run Code Online (Sandbox Code Playgroud)
如果我需要类型而不是常量怎么办?
decltype(common_type(std::declval<T>(), std::declval<U>()))
Run Code Online (Sandbox Code Playgroud)
我也没有看到问题(是的,我在这里使用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));
Run Code Online (Sandbox Code Playgroud)
什么!骗子!没有定义特征!
嗯……其实,这就是重点。这样一来decltype
,许多特征就变得多余了。
干燥的!
继承才有效!
采用基本的类层次结构:
struct Base {};
struct Derived: Base {};
struct Rederived: Derived {};
Run Code Online (Sandbox Code Playgroud)
并定义一个特征:
// 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> {};
Run Code Online (Sandbox Code Playgroud)
注意:特性 forDerived
不直接声明true
,或者false
而是从其祖先那里获取行为。这样,如果祖先改变立场,整个层次结构就会自动跟随。大多数时候,由于基本功能是由祖先提供的,因此遵循其特征是有意义的。对于类型特征更是如此。
// function version
constexpr bool some_trait(...) { return false; }
constexpr bool some_trait(Base const&) { return true; }
Run Code Online (Sandbox Code Playgroud)
注意:省略号的使用是有意的,这是包罗万象的重载。模板函数比其他重载(不需要转换)是更好的匹配,而省略号始终是最差的匹配,保证它只选择那些不适合其他重载的函数。
我想没有必要精确说明后一种方法有多简洁?您不仅摆脱了template <>
杂乱,还可以免费获得继承权。
可以
enable_if
这样实施吗?
不幸的是,我不这么认为,但正如我已经说过的:这不是一个特质。该std
版本可以很好地工作,constexpr
因为它使用bool
参数,而不是类型:)
所以为什么?
好吧,唯一的技术原因是代码的很大一部分已经依赖于历史上作为类型 () 提供的许多特征,std::numeric_limit
因此一致性将决定它。
此外,它使迁移变得boost::is_*
如此容易!
我个人确实认为这是不幸的。但我可能比一般公司更渴望审查我编写的现有代码。