【问题标题】:Use 'class' or 'typename' for template parameters? [duplicate]对模板参数使用“类”或“类型名”? [复制]
【发布时间】:2010-09-17 19:22:05
【问题描述】:

可能重复:
C++ difference of keywords ‘typename’ and ‘class’ in templates

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

template <class T> ...

或者可以这样写:

template <typename T> ...

是否有充分的理由偏爱其中一个?


我接受了最受欢迎(和有趣)的答案,但真正的答案似乎是“不,没有充分的理由偏爱其中一个。”

  • 它们是等价的(除了下面提到的)。
  • 有些人总是有理由使用typename
  • 有些人有理由总是使用class
  • 有些人有理由同时使用这两种方法。
  • 有些人不在乎他们使用哪一种。

但是请注意,在 C++17 之前,对于 模板模板 参数,需要使用 class 而不是 typename。请参阅下面的user1428839's answer。 (但这种特殊情况不是偏好问题,而是语言的要求。)

【问题讨论】:

  • 我认为在这种情况下,将所有答案放在一起并接受您自己的新答案而不是将答案放在问题文本中可能是合理的。
  • 这不是真正的重复。一个人问哪个是更可取的。另一个要求差异。
  • 我不明白这怎么可能与一年多后提出的问题重复。
  • 保持一致就是我想说的。我是一个打字男孩,但这取决于你。
  • 这个“可能的重复”是在 2008 年提出的。“原版”是在 2010 年提出的。他们在 2012 年注意到了质量。

标签: c++ templates


【解决方案1】:

Stan Lippman 谈到了这个here。我觉得这很有趣。

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

【讨论】:

  • 不要忘记阅读 cmets 是否有充分的理由使用“class”而不是“typename”。
  • 复兴!老实说,我觉得那篇文章不是特别清楚。在T::A *obj; 中,据我所知,由于声明规则,该语言应该将声明解析为声明:任何看起来像声明的东西,即使它看起来有点像其他东西,也应该解析为声明[0] .我也没有发现 Bjarne Stroustrup 清楚这一点。 [0] C++ 编程语言 3e,Stroustrup,附录 C.13.5,p。 856-858
  • ...更重要的是,Stan Lippman 在文章中说“这种困境在泛型中是不可能的——没有办法安全地验证任何 T 是否包含 A,以便运行时可以安全地构造一个泛型类型的实例”。从什么时候开始在运行时实例化泛型类型?它们在编译时被实例化,正是因为这个原因,没有export 关键字,我们必须在头文件中定义模板。然后,人们会期望编译器知道关于实例化泛型的所有信息——包括T::A 是什么,一个类型或一个实例。
  • @wilhelmtell 没有这样的规则。该规则仅适用于将函数转换为第一个子表达式的表达式。在您的示例中,如果 T::A 是一种类型,则构造 T::A *obj 在当前标准中实际上是模棱两可的(正如几周前我与核心小组讨论时所发现的那样)。然而,由于每个人都知道它应该如何解析,他们并没有让标准说明它应该如何解析。当f 是一种类型时,与f() 相同(可能是无效的函数调用并且可能是函数转换)。
  • 即使在编辑之后,这个答案充其量也是令人印象深刻的。
【解决方案2】:

根据 Scott Myers,Effective C++(第 3 版)第 42 条(当然,这必须是最终答案)——区别在于“无”。

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

【讨论】:

  • 我喜欢提示因子的概念。我想我会开始使用它。
  • “C++ 模板完整指南”David Vandevoorde Nicolai M. Josuttis。 2.1.1
  • 但是现在我们有了 static_assert(std::is_class::value, "T must be a class");
【解决方案3】:

作为对上述所有帖子的补充,在处理 template template 时,必须使用 class 关键字 (直到并包括 C++14)参数,例如:

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

在本例中,typename Container 会产生编译器错误,如下所示:

error: expected 'class' before 'Container'

【讨论】:

  • 看起来 Clang 3.5 和 Visual Studio 2015 现在在模板模板参数中支持 N4051 类型名。
  • 至少就目前而言,这非常重要,并且使这个答案比公认的第二高答案更好。
【解决方案4】:

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

【讨论】:

  • 当然,typename 也是重载的......
  • 是的,但它似乎不那么令人困惑地重载 - typename 的其他用途令人困惑,不是因为重载,而是因为需要它的情况非常令人困惑。其他关键字重载(classstatic)似乎是混乱的积极参与者。
  • 我必须同意在这里使用typename——使用类似乎过度使用了这个关键字,尤其是在template&lt;class X&gt; class Y { ...的实例中
  • 就我个人而言,如果它使用typename 作为模板参数,我发现它更容易浏览代码。如果我只是浏览一下,寻找类定义或声明,那么每次看到class 时都必须仔细查看。同时,每当我看到typename 时,我都会不由自主地想,“哦,它要么是模板声明,要么是其中一种不稳定的情况。”相反,在浏览模板时,class 可能表示也可能不表示模板,但您知道 typename 只能在模板运行时使用。
【解决方案5】:

不同,你应该更喜欢class 而不是typename

但是为什么呢?

typename对于模板模板参数是非法的,所以为了保持一致,你应该使用class

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

【讨论】:

  • 请注意,这种不一致将由 C++1z 修复。
  • @MilesRout C++1z == C++17,对吧?
  • C++0x,后来变成了 C++1x,就是 C++11。 C++1y 是 C++14,当前版本。 C++1z 很可能是 C++17,假设它不会滑到十年后期。
【解决方案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则没有这种重载的含义。

那个...或者我只是内心懒惰。我键入 'class' 的频率远高于 'typename',因此发现它更容易键入。或者这可能是我写太多OO代码的迹象。

【讨论】:

  • 我不认为这是超载的。在这两种情况下,typename 的作用都是一样的:表示它后面是一个类型而不是一个变量。
  • 但是“typedef”后面总是跟一个类型,那为什么这里需要“typename”呢?如果有必要告诉解析器它是指针声明而不是乘法表达式,我可以理解它在 typename qwert::yuiop * asdfg; 之类的东西中是必需的。但是在 typedef 中没有这样的歧义。
  • 您可能是正确的,我不确定在我的示例中是否严格要求。您的示例非常出色,如“qwerty::yuiop * asdfg;”可能是声明一个指针变量,或者调用乘法运算符。
  • 应该写成typedef integer_traits&lt;IntegerType&gt; traits
【解决方案7】:

只是纯粹的历史。 Quote from Stan Lippman:

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

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

template <class T>
class Demonstration { 
public:
void method() {
   T::A *aObj; // oops ...
};

【讨论】:

    【解决方案8】:

    一点都没有关系,但是 class 让它看起来 T 只能是一个类,而它当然可以是任何类型。所以 typename 更准确。另一方面,大多数人使用类,所以一般来说可能更容易阅读。

    【讨论】:

    • 好吧,另一个人可能会争辩说,使用“类”的可能性使得类看起来只能用于类,因此“类型名”仅限于原始类型。所以这个论点有点主观......
    • 人们可以指出每个class 名称都是typename,但不是每个typename 都是class 名称。如果仅考虑这一点,则有两种合乎逻辑的方法:1) 始终使用 typename,除非在 C++17 之前的代码中使用模板模板参数,或者 2) 如果参数被明确认为是用户定义的类型,或typename 在其他情况下。当然,现实世界并非如此非黑即白,使用class 而不是typename 也有合理的论据,因此最好使用您或您的团队最舒适的那个与。
    • 我个人觉得typename 更容易解析,因为它的含义总是与模板有关。另一方面,其他人发现class 更容易解析,一个原因是typename 中存在固有的歧义,因为它也是使用在模板化类型中定义的类型的关键字。因此,这主要是一个主观问题,除非在一种情况下,语言明确要求其中之一。
    【解决方案9】:

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

    【讨论】:

      【解决方案10】:

      扩展DarenW的评论。

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

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

      模板char myc = '/' >

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

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

      【讨论】:

        猜你喜欢
        • 2014-01-29
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2017-11-17
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多