【问题标题】:What are the use cases of not declaring the name for a typename in template?没有在模板中声明类型名的名称的用例是什么?
【发布时间】:2011-07-03 05:15:12
【问题描述】:

有时我会看到以下声明:

template<typename>  // <-- not "typename T"
struct A { ... };

这种声明的用例是什么。这些有用还是只是风格问题?

【问题讨论】:

    标签: c++ templates coding-style typename


    【解决方案1】:

    您是否真的看到了模板定义的用途,而不是模板声明(仅)?

    一些用途:

    // declaration only: the parameter name has no use beyond documentation
    template<typename>
    struct A;
    
    // this is fine
    template<typename T>
    void eat_an_a(A<T> a);
    
    // later, we can name the parameter to use it
    template<typename T>
    struct A { ... };
    
    // C++0x only
    template<
        typename T
        // We don't care for the actual type (which will default to void)
        // the goal is sfinae
        , typename = typename std::enable_if<
            std::is_array<typename std::decay<T>::type>::value
        >::value
    >
    void
    f(T&& t);
    
    // We still don't care to name that defaulted parameter
    template<typename T, typename>
    void f(T&& t)
    { ... }
    

    Johannes 已对您链接到的非常特殊的案例进行了解释,但显然您发现它并不令人满意。我将向您介绍这是如何工作的。让我们假设一个任意的特征类:

    // no definition
    template<typename TypeToExamine, typename ImplementationDetail = void>
    struct trait;
    

    我在名称中说明了类型参数的作用。现在这个声明允许的,因为第二个参数是默认的,是一些语法糖。无论trait&lt;U&gt; 出现在哪里,它完全正确就好像我们已经写了trait&lt;U, void&gt;。现在让我们为我们的 trait 的基本情况提供一个定义:

    // assume previous declaration is still in scope so we do not default
    // the second parameter again
    template<typename T, typename> struct trait: std::false_type {};
    

    这不是一个非常有用的特性。现在,当我们编写trait&lt;U&gt;trait&lt;U, void&gt; 的缩写)时,我们最终得到了这个定义。这意味着trait&lt;U&gt;::value 是有效的,实际上是false。让我们通过添加秘诀让我们的课程更有用:

    template<typename> struct void_ { typedef void type; };
    // again, assume previous declarations are in scope
    template<typename T, typename void_<decltype( T() + T() )>::type>
    struct trait: std::true_type {};
    

    同样,当我们写trait&lt;U&gt; 时,就好像我们写了trait&lt;U, void&gt;。部分专业化不会改变这一点(不允许这样做)。但是我们查询trait&lt;U&gt;::value时应该使用什么定义呢?好吧,首先,我们必须知道专业化应该匹配什么;或者,神秘的第二个论点typename void_&lt;decltype( T() + T() )&gt;::type是什么?

    最简单的情况是U() + U() 格式错误。然后 SFINAE 介入,就好像专业化不存在一样;因此我们得到了非专业定义,valuefalse。然而,如果U() + U() 是格式良好的,那么decltype 会产生一个类型,并且整个变成void,因为对于所有类型void_&lt;T&gt;::typevoid。所以这意味着我们有一个专门的形式trait&lt;T, void&gt;。这可以匹配trait&lt;U&gt;,只需将TU 匹配。现在valuetrue

    如果专业化已经被编写了

    template<typename T>
    struct trait<T, decltype( T() + T() )>: std::true_type {};
    

    那么它的唯一使用方法是写trait&lt;U, decltype(U() + U())&gt;除非decltype(U() + U())碰巧是无效的。请记住,trait&lt;U&gt;trait&lt;U, void&gt; 的糖。所以trait&lt;int&gt; 永远不会匹配我们的专业化,因为后者的形式是trait&lt;int, int&gt;

    因此,void_ 扮演的角色是始终具有 trait&lt;T, void&gt; 形式的专业化,如果它们没有被 SFINAE 淘汰的话。由于我们根本不关心使用类型参数,所以它没有命名。

    【讨论】:

    • 不,我只在前向声明中没有看到。它也用于定义class 正文。见这里:stackoverflow.com/questions/6540948/…
    • @iammilind 应该将该链接放入问题中!然后我们可以直接指出您从未见过template&lt;typename&gt; struct A ...。你看到了template&lt;typename = void&gt; struct A ...。这非常不同,部分描述在链接内的 cmets 中,您可以在我的回答中看到匿名默认参数的另一种用法。
    • typename = void 无关。见答案:template&lt;typename&gt; struct void_ { typedef void type; };
    • @iammilind 这是一个非常具体的用途。解释有帮助吗?
    【解决方案2】:
    1. 这可以从A 的类型-> 静态成员创建编译时映射。

      template<typename>
      struct sort_statistics { static size_t times_comparator_called = 0; };
      
      template<typename T>
      size_t sort_statistics<T>::times_comparator_called;
      
      template<typename Titer>
      void my_sort( Titer first, Titer last )
      {
          //...
          ++sort_statistics<iterator_traits<Titer>::value_type>::times_comparator_called;
          if (*itA < *itB) { 
          //...
      }
      
    2. 它仍然可以被特化。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2013-03-03
      • 1970-01-01
      • 2016-01-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多