自从海湾合作委员会抓住我这个问题已经有一段时间了,但它刚刚发生在今天.但是我从来没有理解为什么GCC在模板中需要typedef typename,而VS和我猜ICC没有.typedef typename是一个"bug"还是一个严格的标准,还是留给编译器编写者的东西?
对于那些不知道我的意思的人来说,这是一个样本:
templatebool find(const std::map & container, const KEY& key) { std::map ::const_iterator iter = container.find(key); return iter!=container.end(); }
上面的代码在VS(可能在ICC中)编译,但在GCC中失败,因为它想要这样:
templatebool find(const std::map & container, const KEY& key) { typedef typename std::map ::const_iterator iterator; //typedef typename iterator iter = container.find(key); return iter!=container.end(); }
注意:这不是我正在使用的实际功能,而只是一些愚蠢的东西来证明这个问题.
typename是标准所必需的.模板编译需要两步验证.在第一次传递期间,编译器必须验证模板语法,而不实际提供类型替换.在此步骤中,假定std :: map :: iterator是一个值.如果它确实表示类型,则typename关键字是必需的.
为什么这有必要?在替换实际的KEY和VALUE类型之前,编译器不能保证模板不是专用的,并且专门化不会将iterator关键字重新定义为其他类型.
您可以使用以下代码进行检查:
class X {}; templatestruct Test { typedef T value; }; template <> struct Test { static int value; }; int Test ::value = 0; template void f( T const & ) { Test ::value; // during first pass, Test ::value is interpreted as a value } int main() { f( 5 ); // compilation error X x; f( x ); // compiles fine f: Test ::value is an integer }
最后一次调用失败,并显示错误,指示在f()的第一个模板编译步骤中,Test :: value被解释为一个值,但实例化具有类型X的Test <>模板会生成一个类型.
好了,GCC实际上并不需要的typedef
- typename
就足够了.这有效:
#include#include
这是上下文敏感的解析问题的示例.有问题的行只是在这个函数的语法中不明显 - 你需要知道是否std::map
是一个类型.
现在,我似乎无法想到一个例子,::const_iterator
除了一个类型之外可能是什么...... 这也不是一个错误.所以我想编译器可以发现它必须是一个类型,但对于糟糕的编译器(编写器)来说可能很难.
根据标准typename
第14.6/3节的要求,该标准要求使用此处.