【问题标题】:Difference of keywords 'typename' and 'class' in templates?模板中关键字“typename”和“class”的区别?
【发布时间】:2011-01-02 16:44:51
【问题描述】:

对于模板,我已经看到了两种声明:

template < typename T >
template < class T >

有什么区别?

在以下示例中,这些关键字究竟是什么意思(取自德语维基百科关于模板的文章)?

template < template < typename, typename > class Container, typename Type >
class Example
{
     Container< Type, std::allocator < Type > > baz;
};

【问题讨论】:

    标签: c++ templates keyword


    【解决方案1】:

    typenameclass 在指定模板的基本情况下可以互换:

    template<class T>
    class Foo
    {
    };
    

    template<typename T>
    class Foo
    {
    };
    

    是等价的。

    话虽如此,在特定情况下typenameclass 之间存在差异。

    第一个是依赖类型的情况。 typename 用于在引用依赖于另一个模板参数的嵌套类型时声明,例如本例中的 typedef

    template<typename param_t>
    class Foo
    {
        typedef typename param_t::baz sub_t;
    };
    

    您在问题中实际显示的第二个,尽管您可能没有意识到:

    template < template < typename, typename > class Container, typename Type >
    

    当指定一个模板模板时,class 关键字必须像上面那样使用——在这种情况下它不能typename 互换(注意:由于 C++17 在这种情况下允许使用这两个关键字).

    您还必须在显式实例化模板时使用class

    template class Foo<int>;
    

    我确定我还漏掉了其他一些情况,但最重要的是:这两个关键字并不等价,这些是一些常见情况,您需要使用其中的一个。

    【讨论】:

    • 最后一个几乎是一个特殊情况,即您必须使用类或结构,而不是类型名来定义一个类。显然,您的前两位代码都不能用template &lt;typename T&gt; typename Foo {}; 替换,因为 Foo 绝对是一个类。
    • std::vector&lt;int&gt;::value_type 不是依赖类型,你不需要typename - 如果类型依赖于模板参数,你才需要它,比如template&lt;class T&gt; struct C { typedef typename std::vector&lt;T&gt;::value_type type; };
    • 再一次,param_t 不是依赖类型。依赖类型是依赖于模板参数的名称,例如foo&lt;param_t&gt;::some_type,不是模板参数本身。
    • C++1z 提案N4051 将允许您使用typename,即template &lt;typename&gt; typename C
    • GCC 5 开始,G++ 现在允许在模板模板参数中使用 typename
    【解决方案2】:

    对于命名模板参数,typenameclass 是等价的。 §14.1.2:

    没有语义差异 在类和类型名之间 模板参数。

    typename 但是,在使用模板时可以在另一个上下文中使用 - 提示编译器您指的是依赖类型。 §14.6.2:

    模板声明中使用的名称 或定义,这取决于 假定模板参数不 命名一个类型,除非适用的名称 查找查找类型名称或名称 由关键字 typename 限定。

    例子:

    typename some_template<T>::some_type
    

    如果没有typename,编译器通常无法判断您是否指的是类型。

    【讨论】:

    • 我理解规则,但究竟是什么阻止了编译器在内部将 some_template 视为一种类型?抱歉,如果我遗漏了一些明显的东西。
    • @batbrat Here 是关于该主题的详尽答案。例如,some_template&lt;T&gt;::something * p; 可能是指针声明或乘法。
    • 谢谢@AlexChe 我会浏览链接的!
    【解决方案3】:

    虽然没有技术上的区别,但我看到这两者用来表示略有不同的事物。

    对于应该接受任何类型为 T 的模板,包括内置函数(例如数组)

    template<typename T>
    class Foo { ... }
    

    对于仅适用于 T 是真实类的模板。

    template<class T>
    class Foo { ... }
    

    但请记住,这纯粹是一些人使用的一种风格。标准未强制要求或编译器强制执行

    【讨论】:

    • 我不怪你提到它,但我认为这个政策非常误导,因为程序员最终会花时间思考一些无关紧要的事情(“我用对了吗? ") 表示无关紧要的事情(“是否存在实现此模板参数所需接口的内置类型?”)。如果使用了模板参数的任何成员(T t; int i = t.toInt();),那么您需要一个“真正的类”,如果您为T 提供int,您的代码将无法编译...
    • 如果你想限制在实际类中的使用,你最好添加一个专门化来抛出/导致非类类型的错误。如果你想限制使用特定的类,只针对它们。无论如何,这种风格上的区别太微妙了,无法传达信息。
    • 因为它们的意思相同,所以请只使用一个。否则,就像使用 inline {,除非是星期二,然后使用下一行 {。
    • +1 我有时自己会这样做...class 暗示您不仅期望一个“值”可能支持某些运算符、复制或移动构造和/或赋值,而是特别需要支持某些成员访问语义的类型。对声明的最快浏览就会设定期望并阻止例如为class 参数提供内置类型肯定会出错。
    • 我想了解现实世界中存在哪些类型的模板适用于任何类但不适用于内置类型的情况。你有例子吗?
    【解决方案4】:
    1. 没有区别
    2. 模板类型参数Container 本身就是一个带有两个类型参数的模板。

    【讨论】:

    • 大体上有区别。
    • 容器模板化的这两个参数也可以命名吗?在示例中,它们没有任何名称。而且 - 在这个例子中它被写成'class Container' - 也可以写成'typename Container'吗?
    • @Mat:是的,要搜索的词是模板模板参数/参数。例如:template&lt;template&lt;class U&gt; class V&gt; struct C {};
    【解决方案5】:

    这篇sn-p来自c++入门书。虽然我确信这是错误的。

    每个类型参数前面必须有关键字class或typename:

    // error: must precede U with either typename or class
    template <typename T, U> T calc(const T&, const U&);
    

    这些关键字具有相同的含义,并且可以在模板参数列表中互换使用。模板参数列表可以同时使用两个关键字:

    // ok: no distinction between typename and class in a template parameter list
    template <typename T, class U> calc (const T&, const U&);
    

    使用关键字 typename 而不是 class 来指定模板类型参数似乎更直观。毕竟,我们可以使用内置(非类)类型作为模板类型参数。而且,typename 更清楚地表明后面的名称是类型名称。然而,在模板已经被广泛使用之后,typename 被添加到 C++ 中。一些程序员继续独占使用类

    【讨论】:

      【解决方案6】:

      使用&lt;typename T&gt;&lt;class T&gt;没有区别;即它是 C++ 程序员使用的约定。我自己更喜欢&lt;typename T&gt;,因为它更清楚地描述了它的用途;即定义具有特定类型的模板。

      注意:在声明模板模板参数时必须使用class(而不是typename)的例外情况:

      template <template <typename> class    T> class C { }; // valid!
      
      template <template <typename> typename T> class C { }; // invalid!
      

      在大多数情况下,您不会定义嵌套模板定义,因此任何一个定义都可以使用——只要在您的使用中保持一致即可。

      【讨论】:

      • 这两个定义自 C++17 起有效
      猜你喜欢
      • 2011-05-30
      • 1970-01-01
      • 2015-05-12
      • 2019-07-30
      • 1970-01-01
      • 1970-01-01
      • 2020-05-13
      • 1970-01-01
      • 2017-06-22
      相关资源
      最近更新 更多