【问题标题】:Why is type_traits implemented using structs为什么 type_traits 使用结构实现
【发布时间】:2019-10-20 18:04:28
【问题描述】:

大多数类型特征都是使用结构和模板部分(或完全)特化来制作的。例如std::is_same 实现为

template<typename>
struct is_same : false_type {};
template<typename T>
struct is_same<T, T> : true_type {}; // partial specialization

用法是

bool are_equal = std::is_same<T, U>::value;

.. = std::is_same_v<T, U>;

这两个都有点难看。 起初我认为这是唯一的解决方案,然后我发现变量也可以部分专门化(而函数不能)。 类型转换特征不能(显然)是变量,但是对于“信息”特征,为什么这不会比结构更好?

template<typename>
inline constexpr bool is_same = false;
template<typename T>
inline constexpr bool is_same<T, T> = true;

然后

bool are_equal = std::is_same<T, U>;

【问题讨论】:

    标签: c++ typetraits


    【解决方案1】:

    原因只是历史原因。大多数类型特征首先在 C++11 中添加到库中。后来在 C++14 中添加了变量模板,而改变实现的那一点是不可行的,因为这太向后不兼容了。因此添加了带有_v 后缀的变量,这成为了惯例。

    添加新特征的方式相同,因为在任何库中保持一致性都很重要,更不用说标准库了。

    【讨论】:

    • inline 变量也是 C++17 的一个特性,这也是 _v 模板直到那时才出现的原因。
    【解决方案2】:

    有时,您需要一个类型,而不仅仅是底层的 bool 值。标签调度技术提供了一个简单的例子:

    void foo(std::true_type)  { ... }
    void foo(std::false_type) { ... }
    
    foo(std::is_same<T, S>{});
    

    如果std::is_same 只是一个bool 变量模板,那么这段代码就不会那么优雅了。

    【讨论】:

    • 我不确定为什么该代码特别优雅。在那些不能作为函数内的条件完成的函数中发生了什么?围绕 boolean 值进行标记调度有什么意义?如果它是constexpr 的东西,那么您可以轻松地将布尔值作为非类型模板参数传递并使用if constexpr
    • @NicolBolas,如果这两个 foos 做不同的事情,我宁愿将它们作为两个单独的函数,而不是在一个单独的函数中为 if constexpr 提供两个分支。跨度>
    • 如果他们真的在做如此不同的事情,他们不应该有不同的名字吗?为什么要尝试根据参数的差异来调用其中一个?也就是说,从foo(is_same&lt;T, S&gt;{}) 的读者的角度来看,我希望foo 正在执行,无论这意味着什么。不可能是foo,也可能是non-foo
    • @NicolBolas,他们可以以不同的方式做同样的事情,就像 std::distance 以完全不同的方式对随机访问和转发迭代器做同样的事情。
    • 这是接口与实现的问题。您将根据它使用的迭代器类型的属性以不同的方式实现 distance。但是distance接口 不应向用户公开该详细信息。标记的调度作为该类型的主要接口的一部分向接口公开了实现细节。
    【解决方案3】:

    请注意,类型和非类型参数不可互换。您不能编写同时接受两者的一元模板。如果您要构建一个成熟的编译时计算代数,您需要选择一种参数类型并让您的所有模板只接受它。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 2023-03-17
      • 1970-01-01
      • 1970-01-01
      • 2020-10-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多