"typename"和"class"模板参数之间有什么区别?

760
对于模板,我见过两种声明方式:
template < typename T >
template < class T >

有什么区别?
这个例子中的关键词是什么意思(摘自德语维基百科关于模板的文章)?
template < template < typename, typename > class Container, typename Type >
class Example
{
     Container< Type, std::allocator < Type > > baz;
};
6个回答

602

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

template<class T>
class Foo
{
};

并且

template<typename T>
class Foo
{
};

它们是等价的。

尽管如此,有一些特定情况下typenameclass有所不同。

第一种情况是依赖类型。例如这个例子中的typedef,当你引用一个依赖于另一个模板参数的嵌套类型时,需要使用typename进行声明:

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>;

我相信还有其他情况我没有提到,但归根结底:这两个关键词并不等价,在一些常见情况下你需要使用其中一个。


72
最后一个例子基本上是特殊情况,即必须使用 class 或 struct 来定义类,而不能使用 typename。显然,你前两个代码片段都不能被替换为“template <typename T> typename Foo {}”,因为Foo<T>肯定是一个类。 - Steve Jessop
3
std::vector<int>::value_type 不是一个依赖类型,因此您不需要在那里使用 typename - 只有在类型取决于模板参数时才需要使用,例如 template<class T> struct C { typedef typename std::vector<T>::value_type type; }; - Georg Fritzsche
2
再次强调,param_t 不是一个相关类型(dependent type)。 相关类型指的是依赖于模板参数的名称,例如 foo<param_t>::some_type,而不是模板参数本身。 - Georg Fritzsche
2
一份名为C++1z提案[N4051]的文件将允许您使用typename,即template <typename> typename C - user4112979
4
GCC 5开始,G++现在允许在模板模板参数中使用typename关键字. - Chnossos
@SteveJessop,template <typename T> typename Foo {}; 因为Foo<T>绝对是一个类。最终,类会成为一种类型,为什么我们不能使用typename呢? - RaGa__M

129

为了给模板参数命名,typenameclass是等价的。根据 §14.1.2:

在模板参数中,classtypename之间没有语义上的区别。

然而,在另一种使用模板的情况下,可以使用typename来提示编译器你正在引用一个依赖类型。根据§14.6.2:

在模板声明或定义中使用的名称,如果依赖于模板参数,则假定该名称不代表类型,除非适用的名称查找找到一个类型名称或该名称由关键字typename限定。

举例:

typename some_template<T>::some_type

没有使用typename,编译器通常无法确定您是否引用了一个类型。


8
我了解规则,但是究竟是什么阻止编译器将 some_template<T> 作为类型在内部处理呢?如果我错过了一些显而易见的东西,抱歉。 - batbrat
6
这是关于该主题的详细答案。例如,some_template<T>::something * p; 可能是指针声明或乘法。 - Alex Che
谢谢@AlexChe,我会查看链接的! - batbrat

37

虽然在技术上没有区别,但我看到两者用于表示略有不同的东西。

对于一个应该接受任何类型T(包括内置类型如数组)的模板:

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

对于仅在T为实类时有效的模板。

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

但请记住,这只是一种风格,有些人使用它,但它并非由标准规定或编译器强制执行。


23
我不怪你提到这件事,但我认为这项政策非常误导,因为程序员最终会花费时间思考并且关注一些无关紧要的事情(“我是否使用了正确的东西?”),只是为了指示一些不重要的东西(“是否存在一个内置类型来实现此模板参数所需的接口?”)。如果使用任何模板参数成员(T t; int i = t.toInt();),那么你需要一个“真正的类”,如果你为T提供了int,你的代码将无法编译。 - Steve Jessop
1
如果你想限制使用实际类,最好添加一个专门针对非类类型抛出/引发错误的特化。如果你想限制使用特定类,只需为它们进行特化。无论如何,这种风格上的区别太微妙了,难以传达信息。 - Potatoswatter
4
因为它们的意思相同,请只使用一个。否则,这就像使用内联 { 一样,除非是星期二,然后您需要使用下一行 {. - Paul Draper
1
+1 我有时也会这样做... class 意味着你不仅期望一个“值”,可能支持一些运算符、复制或移动构造和/或赋值,而且特别需要一个支持一些成员访问语义的类型。快速浏览声明就可以设置期望,并阻止例如为 class 参数提供内置类型,当然这肯定是一个错误。 - Tony Delroy
1
我想了解在哪些现实世界的情况下,模板可以适用于任何类,但不能适用于内置类型。你有例子吗? - lfalin

11
  1. 没有区别。
  2. 模板类型参数 Container 本身是一个带有两个类型参数的模板。

4
一般来说有所不同。 - Hassan Syed
这个容器模板的两个参数也可以被命名吗?在这个例子中它们没有任何名称。另外,在这个例子中写的是“class Container”,是否也可以写成“typename Container”? - Mat
2
@Mat:是的,要搜索的术语是模板模板参数/实参。例如:template<template<class U> class V> struct C {}; - Georg Fritzsche

9
使用 <typename T><class T> 并没有区别,它是 C++ 程序员使用的惯例。我个人更喜欢 <typename T>,因为它更清晰地描述了其用途;即使用特定类型定义模板。
注意:有一个例外情况,在声明模板模板参数时必须使用 class(而不是 typename)。
template <template <typename> class    T> class C { }; // valid!

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

在大多数情况下,你不需要定义嵌套模板,所以两种定义方式都可以使用——只要在使用中保持一致即可。

5
自 C++17 起,这两个定义都是有效的。 - Eduard Rostomyan

7

这段代码片段来自于C++ Primer书籍。虽然我确信这是错误的。

每个类型参数必须以关键字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++中的;一些程序员仍然专门使用class。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接