ChatGPT解决这个技术问题 Extra ChatGPT

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

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

template < typename T >
template < class T >

有什么不同?

在以下示例中,这些关键字究竟是什么意思(取自德语维基百科关于模板的文章)?

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

H
Hedede

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

template<class T>
class Foo
{
};

template<typename T>
class Foo
{
};

是等价的。

话虽如此,但在某些特定情况下,typenameclass 之间存在差异。

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

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

我确定还有其他一些我错过的情况,但底线是:这两个关键字并不等效,这些是一些常见的情况,您需要使用其中的一个或另一个。


最后一个几乎是必须使用类或结构而不是类型名来定义类这一事实的特例。显然,您的前两位代码都不能用 template <typename T> typename Foo {}; 替换,因为 Foo<T>绝对是一个类。
std::vector<int>::value_type 不是依赖类型,您不需要 typename - 只有当类型依赖于模板参数时才需要它,例如 template<class T> struct C { typedef typename std::vector<T>::value_type type; };
同样,param_t 不是依赖类型。依赖类型是依赖于模板参数的名称,例如foo<param_t>::some_type,而不是模板参数本身。
C++1z 提案 N4051 将允许您使用 typename,即 template <typename> typename C
GCC 5 开始,G++ 现在允许在模板模板参数中使用 typename
V
Victor2748

对于命名模板参数,typenameclass 是等效的。 §14.1.2:

模板参数中的类和类型名之间没有语义差异。

typename 但是,在使用模板时可以在另一个上下文中使用 - 提示编译器您指的是依赖类型。 §14.6.2:

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

例子:

typename some_template<T>::some_type

如果没有 typename,编译器通常无法判断您是否指的是类型。


我理解规则,但是究竟是什么阻止了编译器在内部将 some_template 视为一种类型?抱歉,如果我遗漏了一些明显的东西。
@batbrat Here 是关于该主题的详尽答案。例如,some_template<T>::something * p; 可以是指针声明或乘法。
谢谢@AlexChe,我会通过链接!
M
Michael Anderson

虽然没有技术上的区别,但我看到这两者用来表示略有不同的事物。

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

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

对于仅适用于 T 是真实类的模板。

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

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


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

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


总的来说是有区别的。
容器模板化的这两个参数也可以命名吗?在示例中,它们没有任何名称。而且 - 在这个例子中它被写成'class Container' - 也可以写成'typename Container'吗?
@Mat:是的,要搜索的术语是模板模板参数/参数。例如:template<template<class U> class V> struct C {};
K
K.K

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


u
user438383

使用 <typename T> OR <class T> 没有区别;即,它是 C++ 程序员使用的约定。我自己更喜欢 <typename T>,因为它更清楚地描述了它的用途;即定义具有特定类型的模板。

注意:在声明模板模板参数时,您必须使用 class(而不是 typename)有一个例外:

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

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

在大多数情况下,您不会定义嵌套模板定义,因此任何一个定义都可以工作——只是在您的使用中保持一致。


从 C++17 开始,这两个定义都有效