在C++中,需要EDCOX1×0个关键字,这样编译器就可以在模板中嵌套类型和嵌套值之间消除歧义。但是,在某些情况下不可能有歧义,例如派生类从嵌套类类型继承时。
1 2 3
| template <class T>
class Derived : public T::type
{ }; |
这里不需要typename关键字,实际上甚至不允许使用。这是有意义的,因为上下文消除了歧义。这里,T::type必须引用类型,因为显然不能从值继承。
我认为对于函数模板参数同样适用。
1 2 3 4 5
| template <class T>
void foo(const T::type& v)
{
} |
在这种情况下,上下文明确说明T::type必须引用类型,因为函数参数不能是值。然而,编译器不接受这一点。它想要const typename T::type&。这似乎不一致。为什么语言允许在继承上下文中而不是在函数参数上下文中隐式假设嵌套类型?在这两种情况下都不会有歧义,那么为什么一种情况下需要typename,而另一种情况下不需要呢?
- 这是一个非常有趣的问题。
- C++委员会的成员是人。发送下一个标准的建议更改。
- @Channel72在我与模板的短暂接触中,我意识到有更多的不一致和非正交规则。我不能说模板设计得不好,但肯定有房间(客房不是一些小盒子房)需要改进。
- 值得注意的一点是,函数参数的类型声明可能比基类复杂得多,包括指针、引用、数组和函数类型。我不认为这些方法可以在块范围内产生像T::A * b;那样的歧义,但至少有一个原因可以处理不同于基类的函数参数。
- 你的话可能适用于其他明确的上下文,如typedef T::type alias或T::type var;。在我看来,最好的方法是使所有内容都具有同构性,并在任何地方强制使用类型名,而不存在当前存在的异常(基本说明符和mem初始值设定项)。当然,这永远不会发生,因为这个更改会破坏大量的代码…
- @72频道…精彩的接球!
- @卢克·图莱尔…不,他的话不适用于他们……想想,如果你写t::type*ptr……假设t::type实际上不是嵌套类型,而是嵌套值。然后"t::type*ptr"将尝试计算两个操作数的乘积!
- @纳瓦兹:是的,你给出的例子是模棱两可的;我不知道我给出的例子是怎样的。当然,区分这两种情况可能会使语法分析变得更加困难,但据我所知,解析C++已经相当困难了,所以…
- @卢克·图莱尔…真的。您的具体示例是明确的,但我认为,标准要求我们使用typename只是为了统一,因为它还使程序员和编译器编写起来容易……所以,我认为,同样的理由可能也适用于函数参数!
如果你稍微改变你的声明,你会得到一个完全不同的故事
1 2
| template <class T>
void foo(T::type& v); |
这已经不含糊了。它可以声明一个类型为void的变量,该变量由位的AND表达式初始化。整个声明将被模板化。当然,从语义上讲,这完全是胡说八道,但从语法上讲,这是可以的。
单个const的出现在语法上使其明确,但它太依赖上下文,无法在编译器中工作。它必须记住它读一个const或任何其他类似的东西,当它解析T::type之后,它需要记住把这个名字作为一个类型。它还将进一步膨胀已经非常复杂的标准。
让我们再次更改您的函数声明
1 2
| template <class T>
void foo(const T::type); |
即使在这里出现了const,也没有提供明确的解析。它应该是带有未命名参数的函数声明,还是应该是带有未命中其类型的无效参数名的函数声明?参数的名称由declarator-id解析,它也可以是限定名。因此,在这里,const将属于类型说明符,而在没有typename的情况下,T::type将由编译器解析为参数名。这也完全是胡说八道,但在语法上是有效的。
在基类名称的情况下,名称查找本身声明将忽略非类型名称。因此,可以免费省略typename:名称查找产生的编译器更高级别模块的名称要么引用类型,要么名称查找会给出错误。
我写了一个关于将"模板"和"类型名"放在相关名称上的常见问题解答条目。
- 我不知道我是否应该为揭露这些案件而敬畏,或者真正地为你的理智而恐惧。
- 希望我有超过1个投票权,太棒了!
首先,我不认为有任何意图在只允许类型名的情况(如基类名)和允许非类型实体的情况(如表达式)之间作出尖锐而精确的区分。我想说的是,基类名上下文是由于其他原因而被挑选出来的。
其次,在函数参数声明中,每个实体都必须是一个类型名,这种说法并不完全正确。可以如下声明参数
1 2
| template <class T>
void foo(const T::type& v[T::value]); |
当然,本例中的语法明确规定type必须是一个类型名,value必须是一个值。然而,编译器只能在声明的句法分析之后才能理解,而我相信引入typename的思想是为了帮助编译器实际开始对代码进行正确的句法分析,即在句法分析之前应该有区别,作为句法分析的输入。这种区别可能对代码的解释产生深远的影响。
很有意思的发现是什么导致了这种情况。
我一直在尝试阅读标准来寻找答案,请注意,我是新手。
但是我相信我找到了一个相关的条款。
§14.6.2. A name used in a template
declaration or definition and that is
dependent on a template-parameter is
assumed not to name a type unless the
applicable name lookup finds a type
name or the name is qualified by the
keyword typename.
我想这意味着问题在于名称查找对于基本说明符列表和函数参数的工作方式的不同。
基本说明符名称查找:
§10.2. During the lookup for a base
class name, non-type names are ignored
(3.3.10).
这就解释了为什么基本说明符不需要类型名。
仍在查找函数参数名称查找。
如果这是一个不正确或不相关的假设,请纠正我。同时我一边挖一边挖。
在函数声明中不限定模板参数时,vs2010给出的错误如下:
'T::type' : dependent name is not a
type prefix with 'typename' to
indicate a type.
但是,我仍然不清楚依赖函数参数名称查找的规则…
- 此讨论不可错过的标准要求:C++ 03 146p5:"在基本说明符或MEM初始化器中不允许关键字EDCOX1 OR 0";在这些上下文中,依赖于模板参数的合格ID(14.62-TEMP.DEP)隐含地被假定为类型名。