为什么type_traits用专门的模板结构而不是constexpr实现?

时间:2012-01-17 14:49:32

标签: c++ c++11 template-meta-programming typetraits

标准是否有任何理由将其指定为模板struct而不是简单的布尔constexpr

在一个可能在主要问题的答案中得到回答的另一个问题中,如何用非结构版本进行enable_if填充?

5 个答案:

答案 0 :(得分:18)

一个原因是constexpr函数无法提供嵌套的type成员,这在某些元编程情况下很有用。

为了说清楚,我不是只讨论生成类型的转换特征(如make_unsigned),显然不能成为constexpr函数。 所有类型特征提供这样的嵌套type成员,甚至一元类型特征二元类型特征。例如,is_void<int>::typefalse_type

当然,这可以与std::integral_constant<bool, the_constexpr_function_version_of_some_trait<T>()>一起使用,但实际上并不实际。

在任何情况下,如果你真的想要类似函数的语法,那就已经可以了。您可以使用traits构造函数并利用constexpr implicit conversion in 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;

答案 1 :(得分:4)

注意:这最终看起来更像是一个咆哮,而不是一个正确的答案......虽然我确实得到了一些痒,但请原谅我;)

首先,类特征在历史上是使用模板结构完成的,因为它们优先于constexprdecltype。如果没有这两个,使用函数会有更多的工作,尽管is_base_of 的各种库实现在内部使用函数来获得正确的继承。

  

使用函数的优点是什么?

  • 继承正常运作。
  • 语法可以更自然(typename ::type 看起来很愚蠢 TM
  • 现在有很多特征已经过时了

实际上,继承可能是对抗类特征的要点。你需要将所有派生类专门用于 momma ,这很烦人。很烦人。使用函数只需继承特征,如果你想,可以专门设置

  

缺点是什么?

  • 包装! struct trait可以一次嵌入几个类型/常量。

当然,有人可能会说这实际上很烦人:专攻iterator_traits,你常常无偿地从std::iterator_traits 继承来获取默认值。不同的功能可以自然地提供这一点。

  

可以吗?

好吧,总而言之,一切都是基于constexpr的,除了enable_if(但后来,它不是特质),你会去:

template <typename T>
typename enable_if<std::is_integral(T()) and
                   std::is_signed(T())>::type

注意:我在这里没有使用std::declval因为它需要一个未评估的上下文(即大多数是sizeofdecltype)。因此,一个额外的要求(不是立即可见)是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而您不是t使用类型的值。

  

如果我需要链接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,大量特征刚刚变为冗余

<强> DRY

  

继承正常运作!

采用基本的类层次结构:

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的特征不直接表示truefalse,而是取其祖先的行为。这样,如果祖先改变了姿势,整个层次结构将自动跟随。自从基本功能由祖先提供以来,大部分时间都遵循其特征是有意义的。类型特征更是如此。

// 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_*的迁移变得如此简单!

我个人认为这很不幸。但我可能比普通公司更愿意回顾我写的现有代码。

答案 2 :(得分:3)

一个原因是type_traits提案比constexpr提案旧。

另一个是,如果需要,您可以为自己的类型添加特化。

答案 3 :(得分:2)

可能是因为boost已经有了一个用模板实现的type_traits版本。

我们都知道标准委员会中有多少人复制了这一点。

答案 4 :(得分:2)

我认为主要原因是type_traits已经是tr1的一部分,因此基本上保证以大致相同的形式结束标准,因此它早于{{1} }。其他可能的原因是:

  • 将特征作为类型允许在特征类型上重载函数
  • 许多特征(如constexpr)定义remove_pointer而不是type,因此必须以这种方式表达。
  • 具有定义值和特征定义类型的特征的不同接口,似乎是无意义的
  • value可以是部分专业化的,而功能则不能,因此可能会使某些特征更容易实现

对于你的第二个问题:由于templated structs定义了一个enable_if(或者,如果它被传递为false),type中的嵌套typedef确实是要走的路