使用“class”或“typename”作为模板参数?[复制]

Use 'class' or 'typename' for template parameters? [duplicate]

提问人: 提问时间:10/18/2008 最后编辑:18 revs, 8 users 84%Kristopher Johnson 更新时间:9/23/2023 访问量:308479

问:

这个问题在这里已经有答案了:
11年前关闭。

可能的重复:“
typename”和“class”模板参数之间有什么区别?

在 C++ 中定义函数模板或类模板时,可以这样写:

template <class T> ...

或者可以这样写:

template <typename T> ...

有没有充分的理由偏爱其中之一?


我接受了最流行(也是最有趣)的答案,但真正的答案似乎是“不,没有充分的理由偏爱一个而不是另一个。

  • 它们是等效的(除非下面另有说明)。
  • 有些人有理由总是使用 .typename
  • 有些人有理由总是使用 .class
  • 有些人有理由同时使用两者。
  • 有些人不在乎他们使用哪一个。

但请注意,在 C++17 之前,对于模板模板参数,需要使用 instead of 代替。请参阅下面的 user1428839 的答案。(但这种特殊情况不是偏好问题,而是语言的要求。classtypename

C++ 模板

评论

6赞 Catskul 10/5/2009
我认为在这种情况下,将所有答案放在一起并接受您自己的新答案而不是将答案放在问题文本中可能是合理的。
10赞 Sebastian Mach 2/28/2013
这并不是真正的重复。一个人问哪个是可取的。另一个要求差异。
32赞 Marc.2377 11/20/2014
我看不出这怎么可能与一年多后提出的问题重复。
1赞 user4578093 6/29/2015
我只想说保持一致。我我是一个打字男孩,但这取决于你。
6赞 Puddle 6/19/2019
2008 年有人问过这种“可能的重复”。“原版”是在 2010 年被问到的。他们在 2012 年注意到了。质量。

答:

1赞 Grant Limberg 10/18/2008 #1

据我所知,你使用哪一个并不重要。在编译器眼中,它们是等价的。使用您喜欢的任何一个。我通常使用类。

446赞 itsmatt 10/18/2008 #2

斯坦·李普曼(Stan Lippman)在这里谈到了这一点。我觉得这很有趣。

摘要:Stroustrup 最初用于在模板中指定类型,以避免引入新关键字。委员会中的一些人担心,这种关键词的超载会导致混乱。后来,委员会引入了一个新的关键字来解决句法歧义,并决定让它也用于指定模板类型以减少混淆,但为了向后兼容,保留了其重载的含义。classtypenameclass

评论

9赞 Michael Burr 10/18/2008
别忘了阅读评论,了解是否有充分的理由使用“class”而不是“typename”。
2赞 wilhelmtell 4/9/2010
复兴!老实说,我不觉得那篇文章特别清楚。据我所知,在语言中,由于声明规则,应该将语句解析为声明:任何看起来像声明的东西,即使它模棱两可地看起来像其他东西,也应该解析为声明[0]。我也没有发现 Bjarne Stroustrup 对此很清楚。[0] C++ 编程语言 3e,Stroustrup,附录 C.13.5,第 856-858 页T::A *obj;
1赞 wilhelmtell 4/9/2010
...此外,Stan Lippman 在文章中说:“这种困境在泛型中是不可能的——没有办法安全地验证任何 T 是否包含 A,以便运行时可以安全地构造泛型类型的实例”。从什么时候开始在运行时实例化泛型类型?它们是在编译时实例化的,出于这个确切的原因,在没有关键字的情况下,我们必须在头文件中定义模板。在此之后,人们会期望编译器知道有关实例化泛型的所有信息——包括类型或实例是什么。exportT::A
0赞 Johannes Schaub - litb 12/27/2010
@wilhelmtell没有这样的规则。该规则仅涉及将函数强制转换为第一个子表达式的表达式。在您的示例中,if is a type,该构造在当前标准中实际上是模棱两可的(正如我几周前与核心小组讨论时所证明的那样)。但是,由于每个人都知道应该如何解析它,因此他们不会让标准说应该如何解析它。与 when 相同,是一个类型(可以是无效的函数调用,也可以是函数强制转换)。T::AT::A *objf()f
1赞 underscore_d 1/14/2016
即使在编辑之后,这个答案充其量也是平淡无奇的。
326赞 DarenW 10/18/2008 #3

根据斯科特·迈尔斯(Scott Myers)的说法,有效的C++(第3版)第42项(当然,这必须是最终答案) - 区别是“没有”。

建议使用“class”,如果预期 T 将始终是一个类,则使用“typename”,如果可能期望其他类型(int、char* 等)。将其视为使用提示。

评论

57赞 Martin York 10/18/2008
我喜欢提示因素的概念。我想我会开始使用它。
2赞 bartolo-otrit 9/24/2011
“C++ 模板:完整指南”,David Vandevoorde,Nicolai M. Josuttis。2.1.1
12赞 paulm 2/23/2016
但是现在我们有 static_assert(std::is_class<T>::value, “T must be a class”);
2赞 John P 12/26/2017
它也会变得毛茸茸的。stackoverflow.com/a/46401548/1043529
51赞 Michael Burr 10/18/2008 #4

我更喜欢使用typename,因为我不喜欢重载的关键字(天哪 - 对于各种不同的上下文有多少不同的含义?static

评论

41赞 James Curran 10/18/2008
当然,typename 也是重载的。
8赞 Michael Burr 10/18/2008
没错,但它似乎不那么令人困惑 - 的其他用途令人困惑,不是因为重载,而是因为需要它的情况非常令人困惑。其他关键字重载(或)似乎是混淆的积极参与者。typenameclassstatic
6赞 Billy ONeal 3/20/2010
我必须同意在这里使用 - 使用 class 似乎过度使用了这个关键字,尤其是在typenametemplate<class X> class Y { ...
4赞 Justin Time - Reinstate Monica 1/31/2016
就我个人而言,我发现如果代码用于模板参数,则略读代码会更容易。如果我只是略读,寻找类定义或声明,每次看到时都必须仔细查看。同时,每当我看到 时,我都会自动想到,“哦,这要么是模板声明,要么是那些不稳定的情况之一。相反,在浏览模板时,可能表示模板,也可能不表示模板,但您知道只有在模板正在使用时才能使用。typenameclasstypenameclasstypename
8赞 Frederik Slijkerman 10/18/2008 #5

这完全无关紧要,但类使它看起来像 T 只能是一个类,而它当然可以是任何类型。所以 typename 更准确。另一方面,大多数人使用类,因此通常可能更容易阅读。

评论

0赞 euphrat 9/6/2013
好吧,另一个人可能会争辩说,使用“类”的可能性使得 class 看起来只能用于类,因此“typename”仅限于原始类型。所以这个论点有点主观......
0赞 Justin Time - Reinstate Monica 6/30/2016
人们可以说每个名字都是一个名字,但不是每个名字都是一个名字。如果仅考虑这一点,则有两种逻辑方法:1) 始终使用 ,除非在 C++17 之前的代码中使用模板模板参数,或者 2) 如果参数显式应该是用户定义的类型,或者在其他情况下使用。当然,现实世界并不像那样非黑即白,而且也有使用over的有效论据,所以最好使用你或你的团队最舒服的任何一个。classtypenametypenameclasstypenameclasstypenameclasstypename
0赞 Justin Time - Reinstate Monica 6/30/2016
我个人觉得更容易解析,因为它的含义总是与模板有关。另一方面,其他人则发现更容易解析,其中一个原因是 中存在固有的歧义,因为它也是使用模板化类型中定义的类型的关键字。因此,这主要是一个主观问题,除非在一种情况下,语言明确要求一个优于另一个。typenameclasstypename
10赞 Aaron 10/18/2008 #6

作为对 Mike B 的回应,我更喜欢使用“class”,因为在模板中,“typename”具有重载含义,但“class”没有。以这个选中的整数类型为例:

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 代码的迹象。

评论

9赞 Leon Timmermans 10/29/2008
我不认为这是超载的。在这两种情况下,typename 的作用相同:表示它后面跟着一个类型而不是一个变量。
4赞 Stewart 5/16/2011
但是“typedef”后面总是跟着一个类型,那么为什么这里需要“typename”呢?我可以理解它需要在必要时告诉解析器它是一个指针声明而不是乘法表达式。但是在 typedef 中,没有这种歧义。typename qwert::yuiop * asdfg;
1赞 Aaron 5/19/2011
你可能是对的,我不确定在我的例子中是否严格要求它。您的示例更胜一筹,因为“qwerty::yuiop * asdfg;”可能声明了一个指针变量,或者调用了乘法运算符。
0赞 obataku 5/9/2016
这应该读作吗?typedef integer_traits<IntegerType> traits
-6赞 muenalan #7

扩展 DarenW 的评论。

一旦 typename 和 class 不被接受为非常不同,那么严格使用它们可能仍然有效。仅当确实是类时才使用 class,当它是基本类型(如 char)时才使用 typename。

这些类型确实也被接受,而不是 typename

模板< Char Myc = '/' >

在这种情况下,它甚至优于 typename 或 class。

想想“暗示”或对其他人的理解。实际上,考虑到第三方软件/脚本可能会尝试使用代码/信息来猜测模板发生了什么(考虑 swig)。

10赞 5 revs, 4 users 81%user306683 #8

只是纯粹的历史。斯坦·李普曼(Stan Lippman)的引述

这两个关键词的原因是历史性的。在最初的模板规范中,Stroustrup 重用了现有的 class 关键字来指定类型参数,而不是引入一个当然可能会破坏现有程序的新关键字。这并不是说没有考虑一个新关键词 - 只是考虑到它的潜在破坏,它被认为是没有必要的。在 ISO-C++ 标准之前,这是声明类型参数的唯一方法。

但是应该使用 typename 而不是 class! 有关详细信息,请参阅链接,但请考虑以下代码:

template <class T>
class Demonstration { 
public:
void method() {
   T::A *aObj; // oops ...
};
160赞 5 revs, 4 users 79%JorenHeit #9

作为上述所有帖子的补充,在处理模板模板参数时,强制使用关键字(最多包括 C++14),例如:class

template <template <typename, typename> class Container, typename Type>
class MyContainer: public Container<Type, std::allocator<Type>>
{ /*...*/ };

在此示例中,将生成编译器错误,如下所示:typename Container

error: expected 'class' before 'Container'

评论

6赞 Dwayne Robinson 8/26/2015
看起来 Clang 3.5 和 Visual Studio 2015 现在在模板模板参数中支持 N4051 typename。
3赞 underscore_d 1/14/2016
至少就目前而言,这非常重要,使这个答案比公认的和第二高的答案更好。
18赞 2 revsMehrdad #10

这是有区别的,你应该更喜欢.classtypename

但是为什么?

typename对于模板模板参数是非法的,因此为了保持一致,您应该使用:class

template<template<class> typename MyTemplate, class Bar> class Foo { };    //  :(
template<template<class>    class MyTemplate, class Bar> class Foo { };    //  :)

评论

27赞 Miles Rout 6/24/2014
请注意,此不一致将由 C++1z 修复。
1赞 набиячлэвэли 3/26/2015
@MilesRout C++1z == C++17,对吧?
11赞 Miles Rout 3/26/2015
C++0x,即C++1x,是C++11。C++1y 是 C++14,即当前版本。C++1z 很可能是 C++17,假设它不会滑到十年后。