此问题已在此处有答案:
Where and why do I have to put the "template" and "typename" keywords?(10个答案)
上个月关门了。
有时我看到一些真正难以理解的错误信息吐出的gcc
时使用模板.具体来说,我遇到过这样的问题:看似正确的声明会导致非常奇怪的编译错误,这些错误通过在声明的开头加上typename
关键字前缀而神奇地消失了。(例如,就在上周,我声明了两个迭代器作为另一个模板类的成员,我必须这样做)。typename
的故事是什么?
8条答案
按热度按时间z31licg01#
以下是Josuti的书中的引用:
引入关键字
typename
是为了指定后面的标识符是一个类型。请考虑以下示例:在这里,
typename
用于阐明SubType
是class T
的一种类型。因此,ptr
是指向类型T::SubType
的指针。如果没有typename
,SubType
将被视为静态成员。因此将是
T
类型的值SubType
与ptr
的乘积。kmbjn2e32#
Stan Lippman's blog post建议:
Stroustrup重用了现有的class关键字来指定类型参数,而不是引入一个新的关键字,这当然可能会破坏现有的程序。这并不是说没有考虑新的关键字--只是考虑到它潜在的破坏性,它被认为没有必要。在ISO-C++标准之前,这是声明类型参数的唯一方法。
所以基本上Stroustrup重用了 class 关键字,而没有引入一个新的关键字,这个关键字后来在标准中被更改了,原因如下。
正如所举的例子
语言语法将
T::A *aObj;
误解为算术表达式,因此引入了一个新的关键字typename
:它指示编译器将后面的语句视为声明。
既然关键字在工资单上,那么为什么不修复最初决定重用class关键字所造成的混乱呢?
所以我们两样都有
你可以看看this post。它肯定会帮助你。我只是尽我所能地从中提取。
k2arahey3#
考虑代码
不幸的是,编译器并不需要是通灵的,并且不知道T::sometype最终是否会引用类型名称或T的静态成员。所以,我们使用
typename
来告诉它:7eumitmz4#
在某些情况下,当你引用一个所谓的 dependent 类型的成员(意思是“依赖于模板参数”)时,编译器不能总是明确地推断出结果结构的语义含义,因为它不知道这是什么类型的名字(即,无论它是类型的名称、数据成员的名称还是其他事物的名称)。在这种情况下,你必须明确地告诉编译器这个名字属于一个被定义为依赖类型的成员的类型变量,从而消除这种情况的歧义。
例如
在这个例子中,关键字
typename
是编译代码所必需的。当你想引用一个依赖类型的模板成员时,也会发生同样的事情。指定模板的名称。您还必须使用关键字
template
来帮助编译器,尽管它的位置不同在某些情况下,可能需要同时使用
(if语法正确)。
当然,关键字
typename
的另一个作用是用于模板参数声明。cwdobuhd5#
秘密在于模板可以专门用于某些类型。这意味着它也可以为几种类型定义完全不同的接口。例如,你可以写:
有人可能会问为什么这是有用的,事实上:看起来真的没用。但是请记住,例如
std::vector<bool>
,reference
类型看起来与其他T
类型完全不同。诚然,它不会将reference
的类型从一种类型改变为不同的类型,但无论如何,它都可能发生。现在,如果您使用这个
test
模板编写自己的模板,会发生什么呢?这样的事情对你来说似乎没问题,因为你
expect
,test<T>::ptr
是一个类型。但是编译器并不知道,事实上,标准甚至建议他期望相反的结果,test<T>::ptr
不是一个类型。要告诉编译器你期望的是什么,你必须在前面添加一个typename
。正确的模板如下所示底线是:当你在模板中使用嵌套类型的模板时,你必须在之前添加
typename
。(当然,只有当您的模板的模板参数用于内部模板时。tv6aics16#
两种用途:
1.作为
template
参数关键字(而不是class
)typename
关键字告诉编译器标识符是一种类型(而不是静态成员变量)odopli947#
我想所有的答案都提到了
typename
关键字,用于两种不同的情况:a)声明模板类型参数时。例如
它们之间没有区别,它们是完全一样的。
B)模板使用 * 嵌套依赖类型名称 * 之前。
不使用
typename
会导致解析/编译错误。对于第二种情况,我想补充的是,在Scot Meyers的书Effective C++中提到的,在 * 嵌套依赖类型名称 * 之前使用
typename
是一个例外。例外的是,如果你使用 * 嵌套依赖类型名称 * 作为 * 基类 * 或在 * 成员初始化列表 * 中,你不应该在那里使用typename
:**注:**第二种情况使用
typename
(即在嵌套依赖类型名称之前)是不需要的,因为C++20。o2gm4chl8#