9

标准是否有任何理由将它们指定为 templatestruct而不是简单的 boolean constexpr

在另一个问题中,可能会在主要问题的一个很好的答案中得到回答,一个人将如何enable_if处理非结构版本的东西?

4

5 回答 5

19

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

为了清楚起见,我不仅在谈论产生类型并且显然不能成为函数的转换特征(如)。所有类型特征都提供了这样一个嵌套成员,甚至是一元类型特征二元类型特征。例如是.make_unsignedconstexprtypeis_void<int>::typefalse_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;
于 2012-01-17T14:53:36.783 回答
4

注意:这最终看起来更像是一个咆哮而不是一个正确的答案......虽然我确实在阅读以前的答案时感到有些痒,所以请原谅我;)

首先,类特征在历史上是用模板结构完成的,因为它们早constexprdecltype. 如果没有这两个,使用函数就需要做更多的工作,尽管各种库实现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在这里使用,因为它需要一个未评估的上下文(即,sizeofdecltype大部分)。因此,一个额外的要求(不是立即可见的)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不直接声明truefalse而是从其祖先那里获取行为。这样,如果祖先改变立场,整个层次结构就会自动跟随。大多数时候,由于基本功能是由祖先提供的,因此遵循其特性是有意义的。类型特征更是如此。

// 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.

于 2012-01-17T18:51:35.297 回答
3

一个原因是 type_traits 提议比 constexpr 提议更早。

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

于 2012-01-17T14:54:05.003 回答
2

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

我们都知道标准委员会中有多少人复制提升。

于 2012-01-17T14:50:43.733 回答
2

我想说的主要原因是它type_traits已经是标准的一部分,tr1因此基本上可以保证以或多或少相同的形式出现在标准中,所以它早于constexpr. 其他可能的原因是:

  • 将特征作为类型允许在特征类型上重载函数
  • 许多特征(如remove_pointer)定义 atype而不是 a value,因此它们必须以这种方式表达。为定义值的特征和定义类型的特征提供不同的接口似乎是不必要的
  • templated structs可以部分特化,而函数不能,这样可能会使某些特征的实现更容易

对于您的第二个问题:enable_if定义 a type(或不定义,如果它被传递为假) a 内的嵌套 typedefstruct确实是要走的路

于 2012-01-17T14:57:00.010 回答