【问题标题】:Why are C++11 type traits not alias templates?为什么 C++11 类型特征不是别名模板?
【发布时间】:2012-03-18 21:34:32
【问题描述】:

类似问题:Why are type_traits implemented with specialized template structs instead of constexpr?——但答案不同。

我知道alias templates cannot be specialised 因此目前不能直接用于实现类型特征1。然而,这是委员会有意识的决定,据我所知,没有技术上的理由禁止这样做。

那么将类型特征实现为别名模板,简化它们的语法不是更有意义吗?

考虑

 typename enable_if<is_pointer<T>::value, size_t>::type
 address(T p);

 enable_if<is_pointer<T>, size_t> address(T p);

当然,这会在从 Boost.TypeTraits 迁移时引入重大的界面变化——但这真的是个大问题吗?

毕竟,代码无论如何都需要修改,因为类型驻留在不同的命名空间中,并且由于许多现代 C++ 程序员不愿意打开命名空间,因此需要明确限定(如果要更改的话)。

另一方面,它极大地简化了代码。鉴于模板元编程经常变得嵌套、复杂和复杂,显然更清晰的界面是有益的。

我错过了什么吗?如果不是,我将不胜感激,这不仅仅是猜测,而是依赖于(并且可以引用)委员会决策理由的知识。


1 但是间接的很好!考虑:

template <typename T> using is_pointer = typename meta::is_pointer<T>::type;

其中meta::is_pointer&lt;T&gt; 对应于当前的std::is_pointer&lt;T&gt; 类型。

【问题讨论】:

  • "is_pointer&lt;T&gt;" - 您的 enable_if 别名是否将类型作为第一个参数? :)
  • @Xeo:不,但是特征类有一个constexpr 隐式转换运算符。 :)
  • @GMan:模板参数没有隐式转换。 :P 另外,它们是类型。类型不能隐式转换为任何东西!
  • @wilhelmtell 说真的!?除了这个例子清楚地表明它更简短,样板更少?这是一个简单的例子。我见过并使用过很多(!)更复杂的嵌套模板元编程结构。你几乎将垃圾的数量减少了一半。这是一个巨大的改进。
  • 旁注:C++14 将添加enable_if_t 和其他类似的别名。见en.cppreference.com/w/cpp/types/enable_if。一些编译器已经支持这一点。但是,它们具有不同的名称 (_t),因此它们不会破坏现有代码。

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


【解决方案1】:

对您的问题最具体的回答是:从来没有人提议这样做。

C++ 标准委员会是一个由多国、多公司组成的志愿者集合。您将其视为单个组织内的设计委员会。如果没有在标准草案中加入文字的提议,C++ 标准委员会实际上是无能为力的。

我想没有提案的原因是类型特征是一个早期的提案,boost 实现可以追溯到 2000 年左右。模板别名的实现也很晚。许多委员会成员不愿意提出他们没有实施的东西。而且几乎没有机会实施您的提议。

发布 C++11 的压力很大。它确实打算在 2009 年发布,但当发布日期不慎时,除了修复已经在考虑中的功能之外,对工作文件做任何事情都非常困难。在某些时候,您必须将伟大的新想法放在次要位置,以免您永远无法发布。

更新

从 C++14 开始,TransformationTraits(产生类型的那些)现在具有模板别名拼写,例如:

template <bool b, class T = void>
  using enable_if_t = typename enable_if<b,T>::type;

C++1z 工作草案现在为产生值的特征提供了模板变量拼写:

template <class T>
  constexpr bool is_pointer_v = is_pointer<T>::value;

此外,即使在 C++11 中也可以这样做:

typename enable_if<is_pointer<T>{}, size_t>::type
address(T p);

即您可以使用{} 代替::value(假设您的编译器支持constexpr)。在 C++14 中变成:

enable_if_t<is_pointer<T>{}, size_t>
address(T p);

在 C++1z 中:

enable_if_t<is_pointer_v<T>, size_t>
address(T p);

请注意,C++1z 和 C++14 之间的差异非常小,甚至不保存字符,只需将 {} 更改为 _v 并更改放置这两个字符的位置。

【讨论】:

  • “从来没有人提议过……”——认真的吗?我觉得很难相信。我并没有真正密切关注这个过程,但是有 大量 数量的提案。但这仍然是有道理的,特别是考虑到发布压力和别名模板来晚的事实。
  • 让我改写一下:没有提议。因此,我们从未审查过您的提案。你只能怪你自己。 ;-)
  • 啊啊,搞砸了。有人真的应该卷起袖子写一个漂亮的using 包装器。在所有人看着我之前:我没有时间。 :-(
  • Martinho 写了一个 wrapper,也说明 wrapper 语法不能完全替代现有的::type 版本。 flamingdangerzone.com/cxx11/2012/05/29/type-traits-galore.html
  • 一些标准类型特征计划通过N3655: Transformation Traits redux, v2(不包括第 4 节)在下一个标准版本中添加。
【解决方案2】:

类型特征与包括&lt;memory&gt;&lt;functional&gt; 在内的其他几个库一样,都是从C++ TR1 继承的。虽然那是一个不太正式的文档,但它比 Boost 更正式,而且兼容性是值得的。

另外,请注意类型特征都派生自std::integral_constant&lt;bool&gt;,它确实实现了constexprbool 的转换函数。这样至少可以节省 ::value 部分,如果您愿意的话。

【讨论】:

  • 吹毛求疵:没有 C++03 TR1 这样的东西。 TR1 是一份技术报告,独立于 C++03。 TR1 不是一个不太正式的标准。这根本不是一个标准。一份技术报告除了向世界展示委员会对特定领域的兴趣外,什么也不做。 TR1 是一个实验接口。
  • @HowardHinnant:已修复 (?)。我的观点是,委员会对 TR1 进行了投票,所以它肯定已经涉足了。起草时可能只是出于兴趣,但当 C++11 出现时,它几乎完全被采用。
  • 我不认为隐式转换对我的问题有帮助。我同意在某些情况下它可能会使代码更简单,但例如它不允许写 typename enable_if&lt;is_pointer&lt;T&gt; &gt;::type(或者是吗?),因为这需要一个 type (而不是一个值) 转换为bool。如果这是合法的,我会感到惊讶。
  • @KonradRudolph:您确实需要将::value 替换为(),想想看。我还是更喜欢::value
【解决方案3】:

作为一个完整的旁注,因为对于别名可能会或可能不会帮助像 std::is_pointer 这样的特征似乎存在混淆:

您可以走 Boost.MPL 路线并决定使用 Boost.MPL 风格的整数常量,这意味着类型

template<typename Cond, typename Then = void>
using enable_if = typename std::enable_if<Cond::value, Then>::type;

// usage:
template<
    typename T
    , typename = enable_if<std::is_pointer<T>>
>
size_t address(T p);

或者您可以决定使用值来代替

template<bool Cond, typename Then>
using enable_if = typename std::enable_if<Cond, Then>::type;

// can use ::value
template<
    typename T
    , typename = enable_if<std::is_pointer<T>::value>>
>
size_t address(T p);

// or constexpr conversion operator
template<
    typename T
    , typename = enable_if<std::is_pointer<T> {}>
>
size_t address(T p);

请注意,在后一种情况下,不可能使用 enable_if&lt;std::is_pointer&lt;T&gt;()&gt;std::is_pointer&lt;T&gt;() 是一个函数类型(采用 void 并返回 std::is_pointer&lt;T&gt;)并且是无效的,因为我们的别名接受一个值而不是一个类型这个案例。大括号确保它是一个常量表达式。

您可能已经注意到,std::is_pointer 根本无法从模板别名中受益。这并不奇怪,因为有趣的部分是访问::value,而不是::type:模板别名只能对成员类型有所帮助。 std::is_pointertype 成员并不有趣,因为它是一个 Boost.MPL 样式的整数常量(在本例中为 std::true_typestd::false_type),所以这对我们没有帮助。对不起!

【讨论】:

  • 没错,这使得这样一个库的实现更短。实际上我没有想到这一点,因为定义一个 struct 来获得一个 trait 似乎基本上是一个拐杖。但是当然,将它们定义为结构实际上并不比将它们定义为别名模板更短,即使后者可以是专门的(我们在聊天中讨论过这是不可能的)。所以是的,根据别名模板重新定义 enable_ifnot 类型特征很有意义。
  • @KonradRudolph 该标准将其特征分为 UnaryTypeTraitBinaryTypeTraitTransformationTrait。前两个都涉及std::integral_constant,而最后一个暗示有一个“有趣的”type 成员。这些特征是:remove_const (+ add_const, remove_volatile 等), remove_reference (+ add_lvalue_reference 等), make_[un]signed, remove_extent (+ remove_all_extents), add_pointer ( +remove_pointer)。还有一些少数元函数也可以转换为别名,尤其是enable_ifdecay
猜你喜欢
  • 2023-04-04
  • 2021-08-01
  • 2011-06-03
  • 2016-06-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多