注意:这最终看起来更像是一个咆哮而不是一个正确的答案......虽然我确实在阅读之前的答案时感到有些痒,所以请原谅我;)
首先,类特征在历史上是使用模板结构完成的,因为它们早于 constexpr 和 decltype。如果没有这两个,使用函数就需要更多的工作,尽管is_base_of 的各种库实现必须在内部使用函数来获得正确的继承。
使用函数有哪些优势?
-
继承就行了。
- 语法可以更自然(
typename ::type 看起来很蠢 TM)
- 很多特征现在已经过时了
实际上,继承可能是对类特征的主要观点。像momma 一样,你需要专门化你的所有派生类,这很烦人。很烦人。使用函数,您只需继承特征,并且可以专门化如果您愿意。
有什么缺点?
当然,有人可能会说这实际上很烦人:专门化iterator_traits,你只是经常无偿继承std::iterator_traits只是以获得默认值。不同的功能会很自然地提供这一点。
可以吗?
好吧,总而言之,除了enable_if 之外,所有内容都将基于constexpr(但是,这不是一个特征),你会去:
template <typename T>
typename enable_if<std::is_integral(T()) and
std::is_signed(T())>::type
注意:我没有在这里使用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
如果我需要一个类型,而不是常量怎么办?
decltype(common_type(std::declval<T>(), std::declval<U>()))
我也没有发现问题(是的,我在这里使用declval)。但是......传递类型与constexpr无关; constexpr 函数在返回您感兴趣的 值 时很有用。当然可以使用返回复杂类型的函数,但它们不是 constexpr 并且您不使用该值类型。
如果我需要链接 trais 和类型怎么办?
具有讽刺意味的是,这正是函数大放异彩的地方:)
// 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> {};
注意: Derived 的特征不直接声明 true 或 false,而是从其祖先那里获取行为。这样,如果祖先改变立场,整个层次结构就会自动跟随。大多数时候,由于基本功能是由祖先提供的,因此遵循其特征是有意义的。类型特征更是如此。
// function version
constexpr bool some_trait(...) { return false; }
constexpr bool some_trait(Base const&) { return true; }
注意:省略号的使用是有意的,这是包罗万象的重载。模板函数将比其他重载更好地匹配(无需转换),而省略号始终是最差匹配,以确保它仅选择其他重载不适合的函数。
我想没有必要精确说明后一种方法有多简洁?您不仅可以摆脱template <> 的混乱,还可以免费获得继承。
enable_if可以这样实现吗?
不幸的是,我不这么认为,但正如我已经说过的:这不是一个特质。而std 版本与constexpr 配合得很好,因为它使用bool 参数,而不是类型:)
那么为什么?
嗯,唯一的技术原因是大部分代码已经依赖于过去作为类型 (std::numeric_limit) 提供的许多特征,因此一致性将决定它。
此外,它使从boost::is_* 的迁移变得如此简单!
我个人认为这很不幸。但我可能比一般公司更渴望审查我编写的现有代码。