混淆模板错误
我一直在玩clang,我偶然发现了“test / SemaTemplate / dependent-template-recover.cpp”(在clang发行版中),它应该提供一些提示来从模板错误中恢复。
整个事情可以很容易地被剥离到一个最小的例子:
template<typename T, typename U, int N> struct X { void f(T* t) { // expected-error{{use 'template' keyword to treat 'f0' as a dependent template name}} t->f0<U>(); } };
叮当产生的错误信息:
tpl.cpp:6:13: error: use 'template' keyword to treat 'f0' as a dependent template name t->f0<U>(); ^ template 1 error generated.
…但是我很难理解到底在哪里插入template
关键字来使代码在语法上是正确的?
ISO C ++ 03 14.2 / 4:
当成员模板专业化的名称出现后。 或 – 在后缀expression式中,或者在限定id中的嵌套名称说明符之后,并且postfix-expression或qualified-id显式依赖于模板参数(14.6.2), 则成员模板名称必须是由关键字模板作为前缀 。 否则,该名称被假定为命名一个非模板。
在t->f0<U>();
f0<U>
是一个成员模板专门化,出现在->
之后,显式地依赖于模板参数U
,所以成员模板专门化必须以template
关键字为前缀。
所以把t->f0<U>()
改为t->template f0<U>()
。
除了别人提出的观点之外,注意到编译器有时无法下定决心,在实例化时两种解释都可以产生另一种有效的程序
#include <iostream> template<typename T> struct A { typedef int R(); template<typename U> static U *f(int) { return 0; } static int f() { return 0; } }; template<typename T> bool g() { A<T> a; return !(typename A<T>::R*)af<int()>(0); } int main() { std::cout << g<void>() << std::endl; }
这在f<int()>
之前省略template
时打印0
,而在插入时为1
。 我把它作为一个练习来弄清楚代码的作用。
在插入点之前插入它:
template<typename T, typename U, int N> struct X { void f(T* t) { t->template f0<U>(); } };
编辑:如果你像一个编译器一样思考,这个规则的原因就会变得更加清晰。 编译器一般只能一次性向前看一个或两个令牌,一般不会“向前看”expression式的其余部分。 [编辑:查看评论]关键字的原因与为什么你需要typename
关键字来表示相关的types名称相同:它告诉编译器:“嘿,你将要看到的标识符是模板的名称,而是比静态数据成员的名称后跟一个小于号“。
摘自C ++模板
.template构造在引入typename后发现了一个非常类似的问题。 考虑以下使用标准位集types的示例:
template<int N> void printBitset (std::bitset<N> const& bs) { std::cout << bs.template to_string<char,char_traits<char>, allocator<char> >(); }
这个例子中奇怪的构造是.template。 如果没有额外的模板使用,编译器不知道小于号的符号(<)不是真的“小于”,而是模板参数列表的开始。 请注意,只有在期间之前的结构依赖于模板参数,这是一个问题。 在我们的例子中,参数bs取决于模板参数N.
总而言之,.template符号(以及类似符号,如 – > template)只能在模板内部使用,并且只能在遵循依赖于模板参数的内容的情况下使用。