Possible Duplicate:
C++ difference of keywords ‘typename’ and ‘class’ in templates
在C++中定义函数模板或类模板时,可以这样写:
或者你可以这样写:
1
| template <typename T> ... |
有没有一个比另一个更喜欢的理由?
我接受了最受欢迎(也最有趣)的答案,但真正的答案似乎是"不,没有什么好的理由比另一个更喜欢一个"。
- 它们是等效的(除非下文另有说明)。
- 有些人有理由总是使用typename。
- 有些人有理由总是使用class。
- 有些人有理由两者兼用。
- 有些人不在乎他们用的是哪一种。
但是,注意,对于模板模板参数,需要使用class而不是typename。请参阅下面的用户142839的答案。(但这一特殊情况并非偏好问题,而是语言的要求。)(这也将随着c++17的变化而变化)
- 我认为,在这种情况下,把所有的答案放在一起,接受你自己的新答案,而不是把答案放在问题文本中,可能是合理的。
- 这不是真的复制品。那个人问什么时候比较合适。另一个要求区别。
- 我不明白这怎么可能是一年多后问的问题的副本。
- 我只想说的是要始终如一。我是个打字员,但这取决于你。
- 如果我期望的类型是默认的数据类型,如int、float、double等,那么通常我倾向于使用typename,另一方面,如果模板被期望或有理由接受类、结构或用户定义的类型,那么我倾向于使用class。这只是一个自引用,因此我可以区分两个模板之间的目的,即使关键字语法在95%的时间内是可互换的。
斯坦·利普曼在这里谈过这个。我觉得很有趣。
摘要:stroustrup最初使用class在模板中指定类型,以避免引入新的关键字。委员会中的一些人担心这个关键词的过度使用会导致混乱。后来,委员会引入了一个新的关键字typename来解决句法上的歧义,并决定让它也用来指定模板类型以减少歧义,但为了向后兼容,class保留了它的重载含义。
- 别忘了在评论中读到使用"class"而不是"typename"是否有充分的理由。
- 复兴!老实说,我觉得那篇文章不太清楚。据我所知,在T::A *obj;中,语言应该根据声明规则将语句解析为声明:任何看起来像声明的东西,即使它看起来像其他东西,也应该解析为声明[0]。我也没有发现比亚恩·斯特劳斯特鲁普清楚这一点。〔0〕C++程序设计语言3E,Stroustrup,附录C.5.5,P.85-858。
- …更重要的是,StanLippman在文章中说,"这种类型的dilemna在泛型中是不可能的——没有办法安全地验证任何t是否包含a,以便运行时能够安全地构造泛型类型的实例。"从什么时候开始在运行时实例化泛型类型?它们在编译时被实例化,因此没有export关键字,我们必须在头文件中定义模板。然后,我们会期望编译器了解所有关于已实例化的泛型的信息——包括T::A是什么,一个类型或一个实例。
- @威尔赫姆特尔没有这样的规则。规则只涉及将函数强制转换为第一个子表达式的表达式。在您的示例中,如果T::A是一个类型,那么当前标准中的构造T::A *obj实际上是不明确的(正如我几周前与核心组讨论时发现的那样)。然而,由于每个人都知道应该如何解析它,所以他们并没有让标准说明应该如何解析它。当f是一个类型(可能是无效的函数调用,也可能是函数强制转换)时,与f()相同。
- 这种混淆的原因是"id expression"非终端的奇怪用法:标准的某些部分认为规则"id expression"无法解析类型名,而其他部分则认为它可以(有时仅滥用此非终端,因为没有其他东西可供使用)。其结果是,歧义的解决,甚至仅仅是歧义的存在,或多或少是由惯例而不是从标准中推导出来的。所以有些段落认为,由于T::A是一个类型,T::A不能是id表达式,因此T::A *obj不能是乘法。但其他段落与此相矛盾。
- 即使在编辑之后,这个答案充其量也令人印象深刻。
根据Scott Myers,有效的C++(第三ED)项目42(当然,这必须是最终的答案)-差异是"没有"。
建议在需要时使用"class",如果可能需要其他类型(int,char*随便什么),则T将始终是一个类,并使用"typename"。把它当作用法提示。
- 我喜欢暗示因子的概念。我想我会开始用那个。
- "C++模板完全指南"戴维VANDEOORD NICOLAI M.约瑟夫蒂。2.1.1
- 但现在我们有了静态断言(std::is_class::value,"t must be a class");
- 它也会变得多毛。stackoverflow.com/a/46401548/1043529
作为对上述所有帖子的补充,在处理模板模板参数时,强制使用EDCOX1×0关键字(例如,包括C++ 14),例如:
1 2 3
| template <template <typename, typename> class Container, typename Type>
class MyContainer: public Container<Type, std::allocator<Type>>
{ /*...*/ }; |
在本例中,typename Container会生成一个编译器错误,如下所示:
1
| error: expected 'class' before 'Container' |
- 看起来Clang 3.5和Visual Studio 2015现在在模板模板参数中支持N4051类型名。
- 至少就目前而言,这一点非常重要,使这个答案比公认的和第二高的答案更好。
我更喜欢使用typename,因为我不喜欢重载关键字(jeez——static在不同的上下文中有多少不同的含义?).
- 当然,typename也被重载了….
- 是的,但它似乎没有那么令人困惑的超载——typename的其他用途令人困惑,不是因为超载,而是因为需要它的情况是相当混乱的。其他关键字重载(class或static似乎是混乱中活跃的参与者。
- 我同意在这里使用typename--类的使用似乎过度使用了这个关键字,特别是在template class Y { ...的实例中。
- 就我个人而言,如果使用typename作为模板参数,我发现浏览代码更容易。如果我只是略读一下,寻找一个类定义或声明,那么每次看到class时,我都必须仔细观察。同时,每当我看到typename时,我都会自动地想,"哦,这要么是模板声明,要么是那些不稳定的情况之一。"相反,当略读模板时,class可能或不可能表示一种情况,但你知道,typename只能在模板正在运行时使用。
有区别,你应该选择
class而不是
typename。但是为什么呢?
typename对于模板参数是非法的,为了保持一致,您应该使用class:
1 2
| template<template<class> typename MyTemplate, class Bar> class Foo { }; // :(
template<template<class> class MyTemplate, class Bar> class Foo { }; // :) |
- 请注意,这种不一致性将由C++ 1Z来修复。
- @ MySeRouc+1Z==C++ 17,对吗?
- C++0x为C++ 1x,为C++ 11。C++ 1Y是C++ 14,当前版本。C++ 1z最有可能是C++ 17,假设它不会在十年后滑到后面。
纯粹的历史。引自Stan Lippman:
The reason for the two keywords is historical. In the original template specification, Stroustrup reused the existing class keyword to specify a type parameter rather than introduce a new keyword that might of course break existing programs. It wasn't that a new keyword wasn't considered -- just that it wasn't considered necessary given its potential disruption. And up until the ISO-C++ standard, this was the only way to declare a type parameter.
但是应该使用类型名而不是类!有关详细信息,请参见链接,但请考虑以下代码:
1 2 3 4 5 6
| template <class T>
class Demonstration {
public:
void method() {
T::A *aObj; // oops ...
}; |
作为对mike b的回应,我更喜欢使用"class",因为在模板中,"typename"有一个过载的含义,但"class"没有。以这个检查过的整数类型为例:
1 2 3 4 5 6 7 8 9 10 11 12
| template <class IntegerType>
class smart_integer {
public:
typedef integer_traits<Integer> traits;
IntegerType operator+=(IntegerType value){
typedef typename traits::larger_integer_t larger_t;
larger_t interm = larger_t(myValue) + larger_t(value);
if(interm > traits::max() || interm < traits::min())
throw overflow();
myValue = IntegerType(interm);
}
} |
larger_integer_t是一个依赖名称,因此它需要"typename"对其进行处理,以便解析器能够识别larger_integer_t是一个类型。另一方面,类没有这种超负荷的含义。
那个…或者我只是内心的懒惰。我键入"class"的频率远比键入"typename"的频率高,因此发现键入起来容易得多。或者这可能是我写了太多OO代码的迹象。
- 我不认为那是超负荷的。在这两种情况下,typename的作用相同:表示后面跟的是类型而不是变量。
- 但是"typedef"后面总是跟一个类型,所以为什么这里需要"typename"?如果有必要告诉解析器它是一个指针声明而不是乘法表达式,我可以理解在typename qwert::yuiop * asdfg;之类的东西中需要它。但在typedef中没有这样的歧义。
- 你可能是对的,我不确定在我的例子中是否严格要求这样做。您的示例更优,如"qwerty::yuiop*asdfg;",可能声明指针变量,也可能调用乘法运算符。
- 这应该是typedef integer_traits traits吗?
这一点都不重要,但是类使它看起来像是一个类,而它当然可以是任何类型的。所以typename更准确。另一方面,大多数人使用类,因此一般来说,这可能更容易阅读。
- 另一种说法是,使用"class"的可能性使它看起来像类只能用于类,因此"typename"仅限于基元类型。所以这个论点是主观的…
- 可以指出,每个class名都是typename名,但不是每个typename名都是class名。如果仅考虑这一点,有两种逻辑方法:1)始终使用typename,除非在pre-c++17代码中使用模板模板参数;或2)如果参数明确假定为用户定义的类型,则使用class,或在其他情况下使用typename。当然,现实世界并不像那样黑与白,而且在使用typename时也有合理的理由使用class而不是typename,所以最好使用你或你的团队最喜欢的。
- 我个人认为typename更容易解析,因为它的含义总是与模板相关。另一方面,其他人发现class更容易解析,一个原因是typename中存在固有的歧义,因为它也是使用模板化类型内定义的类型的关键字。所以,这主要是一个主观的问题,除了在一种情况下,语言明确要求一个比另一个。
据我所知,你用哪一个并不重要。在编译器看来,它们是等价的。用你喜欢的那种。我通常上课。
扩展darenw的评论。
一旦typename和class不被接受为非常不同,那么严格使用它们可能仍然有效。只有当类确实是类时才使用类,当它是基本类型(如char)时才使用typename。
这些类型确实也被接受,而不是类型名
template< char myc = '/' >
在这种情况下,它甚至优于typename或class。
想想"含蓄"或其他人能理解。实际上,考虑到第三方软件/脚本可能试图使用代码/信息来猜测模板发生了什么(考虑到swig)。