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

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>::typefalse_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)

  • ... grrr ...讨厌冻结...无论如何:`decltype`允许无痛地从函数中获取类型:`decltype(is_void(std :: declval <int>()))`vs`typename is_void <int > :: type` ......看起来和我很相似. (2认同)
  • 哇模板别名比我想象的更具表现力! (2认同)

Mat*_* M. 5

注意:这最终看起来更像是咆哮而不是正确的答案...不过,读之前的答案时我确实有些痒,所以请原谅;)

首先,类特征历来是通过模板结构完成的,因为它们早于constexprdecltype。如果没有这两个,使用函数的工作量会增加一些,尽管各种库实现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在这里使用,因为它需要一个未评估的上下文(即,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
Run Code Online (Sandbox Code Playgroud)

如果我需要类型而不是常量怎么办?

decltype(common_type(std::declval<T>(), std::declval<U>()))
Run Code Online (Sandbox Code Playgroud)

我也没有看到问题(是的,我在这里使用declval)。但是...传递类型与 无关constexprconstexpr当函数返回您感兴趣的时,它们很有用。当然可以使用返回复杂类型的函数,但它们不是,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_*如此容易!

我个人确实认为这是不幸的。但我可能比一般公司更渴望审查我编写的现有代码。