在模板函数参数中使用typename关键字的用法

33
在C++中,typename关键字是必需的,以便编译器可以在模板中区分嵌套类型和嵌套值。然而,在某些情况下,不存在歧义,例如当派生类从嵌套类类型继承时。
template <class T>
class Derived : public T::type
{ };

在这里,不需要使用typename关键字,事实上甚至不允许使用。这是有道理的,因为上下文消除了歧义。在这里,T::type必须引用一种类型,因为显然不能从一个值继承。我认为同样的规则也适用于函数模板参数。
template <class T>
void foo(const T::type& v)
{

}

在这种情况下,上下文表明T::type必须引用一种类型,因为函数参数不能是值。然而,编译器不接受这个。它想要const typename T::type&。这似乎是不一致的。为什么语言允许在继承的上下文中隐含一个嵌套类型,但在函数参数的上下文中却不允许呢?在两种情况下都不存在歧义,那么为什么需要在其中一种情况下使用typename而在另一种情况下不需要呢?

3
这是一个非常有趣的问题。 - James McNellis
1
C++委员会的成员都是人类。请提交下一个标准的建议更改。 - Martin York
值得注意的一点是,函数参数的类型声明可以比基类更复杂,包括指针、引用、数组和函数类型。我不认为它们会像块作用域中的 T::A * b; 那样造成歧义,但至少这是将函数参数与基类视为不同的原因之一。 - aschepler
2
你的评论也适用于其他明确的上下文,例如 typedef T::type aliasT::type var;。在我看来,最好的方法是使一切都变得统一,并强制在任何地方使用typename,而不是目前存在的例外情况(基类说明符和成员初始化器)。当然,这永远不会发生,因为这种改变将破坏大量的代码... - Luc Touraille
@Luc Touraille... 确实。您的具体示例是明确的,但我认为,标准要求我们仅出于统一性而使用typename,因为它使程序员以及编译器编写更轻松...因此,我认为,相同的理由可能适用于函数参数! - Nawaz
显示剩余3条评论
3个回答

26
如果你稍微改变你的陈述,你会得到一个完全不同的故事。
template <class T>
void foo(T::type& v);

这已经不再是一种明确的表达方式了。它可以声明一个类型为void的变量,并通过按位AND表达式进行初始化。整个声明将被模板化。当然,从语义上讲这完全是无意义的,但从语法上讲是可以的。

单独出现一个const在语法上使得它变得明确,但这对于编译器来说过于依赖上下文。它必须记住它读取了一个const或任何其他类似的东西,并且在解析之后的T::type时需要记住将此名称作为类型。这还将进一步使已经复杂的标准变得难以置信。

让我们再次改变你的函数声明

template <class T>
void foo(const T::type);

不要说里面的const的出现都无法提供一个明确的解析。它应该是一个带有无名参数的函数声明,还是一个带有无效参数名且缺少类型的函数声明?参数的名称由declarator-id解析,它也可以是一个限定名称。所以在这里,const将属于类型说明符,而T::type将被编译器解析为参数的名称,在没有typename的情况下。这也完全没有意义,但在语法上是有效的。
在基类名称的情况下,名称查找本身说明非类型名称被忽略。所以你可以免费省略typename:名称查找所产生的名称要么是一个类型,要么名称查找会报错。
我已经写了一个关于在哪里以及为什么我必须使用"template"和"typename"关键字的FAQ条目

3
我不知道是应该因为发现了这些案例而感到敬畏,还是真正担心你的心智健康 :) - Matthieu M.

5
首先,我认为从来没有想过要在仅允许类型名称(如基类名称)的情况和允许非类型实体(如表达式)的情况之间进行明确和精确的区分。我会说,基类名称上下文是因为某些其他原因而被单独挑选出来。
其次,在函数参数声明中,不准确地说每个实体都必须是类型名。您可以按以下方式声明参数。
template <class T>
void foo(const T::type& v[T::value]);

当然,这种情况下的语法明确规定type必须是一个类型名,而value必须是一个值。然而,在语法分析之后,编译器才能弄清楚这一点,而我认为typename的概念是为了帮助编译器实际上开始正确的语法分析代码,即应该在语法分析之前作为语法分析的输入来区分它们。这种区别可能对代码的解释产生深远的影响。

2

很有意思,找到导致这个问题的原因。

我一直在尝试阅读标准以寻找答案,请注意我在这方面是个新手。

然而,我相信我已经找到了一个相关条款。

§14.6.2. 模板声明或定义中使用的依赖于模板参数的名称被认为不是类型名称,除非适用的名称查找找到一个类型名称或该名称由关键字typename限定。

我想这意味着问题在于基类说明符列表和函数参数的名称查找方式不同。

基类说明符名称查找:

§10.2. 查找基类名称时,忽略非类型名称(3.3.10)。

这就解释了为什么基类说明符不需要使用typename

我仍在寻找函数参数名称查找的规则。

如果我的假设是不正确或不相关的,请纠正我。与此同时,我会继续深入挖掘。

在函数声明中没有限定模板参数时,VS2010给出的错误如下:

'T::type' : dependent name is not a type prefix with 'typename' to indicate a type.

然而,我仍不清楚依赖函数参数名称查找的规则...


2
这次讨论中不容忽视的一个标准要求是:C++03 14.6p5:“关键字typename基类说明符成员初始化器中是不允许的;在这些上下文中,依赖于模板参数(14.6.2 temp.dep)的限定标识符被隐式地假定为类型名。” - aschepler

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接