模板中关键字“ typename”和“ class”的区别?


504

对于模板,我看到了两个声明:

template < typename T >
template < class T >

有什么不同?

在下面的示例中这些关键字的确切含义是什么(摘自关于模板的Wikipedia文章)?

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

Answers:


430

typename并且class是在指定模板的基本情况下可以互换:

template<class T>
class Foo
{
};

template<typename T>
class Foo
{
};

是等效的。

话虽如此,在某些情况下,typename和之间存在差异class

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

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

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

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

指定模板模板时class必须如上所述使用关键字- 在这种情况下不能与关键字互换(注意:由于C ++ 17在这种情况下都允许使用两个关键字)typename

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

template class Foo<int>;

我确定还有其他情况,但是最重要的是:这两个关键字不相同,这是一些您需要使用其中一个的常见情况。


45
最后一个几乎是一个特殊情况,您必须使用类或结构而不是类型名来定义一个类。显然,您的前两位代码都不能替换为template <typename T> typename Foo {};,因为Foo <T>绝对是一类。
史蒂夫·杰索普

2
std::vector<int>::value_type不是依赖类型,您不需要typename那里-仅在类型取决于模板参数时才需要它,例如template<class T> struct C { typedef typename std::vector<T>::value_type type; };
Georg Fritzsche 2010年

2
再说一次,param_t不是依赖类型。依赖类型是依赖于模板参数的名称,例如foo<param_t>::some_type,不依赖模板参数本身。
乔治·弗里茨彻

2
C ++ 1z提案N4051将允许您使用typename,即template <typename> typename C
user4112979 2014年

4
截至目前GCC 5G ++现在允许在模板template参数中使用typename
Chnossos 2014年

95

用于命名模板参数,typename并且class是等效的。第14.1.2条:

模板参数中的类和类型名之间没有语义上的区别。

typename但是,在使用模板的另一种情况下是可能的-提示编译器您引用的是依赖类型。第14.6.2条:

除非在适用的名称查找中找到类型名称或该名称由关键字typename限定,否则假定模板声明或定义中使用的且依赖于模板参数的名称不会命名类型。

例:

typename some_template<T>::some_type

没有typename编译器,通常无法判断您是否在引用类型。


2
我理解规则,但是到底是什么阻止编译器在内部将some_template <T>视为类型呢?抱歉,如果我缺少明显的内容。
batbrat

23

虽然没有技术上的差异,但我已经看到两者用来表示略有不同的事物。

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

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

对于仅在T是实类的情况下才有效的模板。

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

但是请记住,这纯粹是某些人使用的样式。未由标准强制要求或由编译器强制执行


15
我不怪您提到它,但是我认为该政策是错误的,因为程序员最终会花时间思考无关紧要的事情(“我使用了正确的?”来表示不正确的事情)。无关紧要(“是否存在实现此模板参数所需接口的内置类型?”)。如果模板参数的任何成员都使用(T t; int i = t.toInt();),那么你需要一个“真正的阶级”,如果你提供你的代码将无法编译intT...
史蒂夫·杰索普

1
如果要将使用限制在实际的类中,则最好添加一个特殊化以引发/引起非类类型的错误。如果您想限制特定类的使用,请仅专门针对它们。无论如何,这种风格上的区别太微妙,无法传达信息。
Potatoswatter 2010年

2
由于它们含义相同,因此请仅使用其中之一。否则,就像使用内联{,除非是星期二,然后再使用下一行{。
保罗·德雷珀

+1我有时会自己做这件事…… class意味着您不仅在期望一个“值”可能支持某些运算符,复制或移动构造和/或赋值,而且还特别需要一种支持某些成员访问语义的类型。然后,快速浏览声明会设置期望值和不鼓励值,例如在class肯定会出现错误的情况下为参数提供内置类型。
Tony Delroy

我想了解在哪种现实情况下,模板可用于ANY类,但不适用于内置类型。你有例子吗?
lfalin

7
  1. 没有不同
  2. 模板类型参数Container本身就是具有两个类型参数的模板。

3
总体上有所不同。
哈桑·赛义德

可以使用容器作为模板的这两个参数也可以命名吗?在示例中,它们没有任何名称。而且-在这个例子中,它被写为“类容器”-是否也可以写成“类型名容器”?

2
@Mat:是的,要搜索的术语是模板模板参数/参数。例如:template<template<class U> class V> struct C {};
Georg Fritzsche 2010年

6

这段代码来自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而不是使用类来指定模板类型参数似乎更直观。毕竟,我们可以将内置(非类)类型用作模板类型参数。此外,typename更清楚地指示后面的名称是类型名称。但是,在模板已经广泛使用之后,typename被添加到C ++中。一些程序员继续专门使用类

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.