混淆模板错误

我一直在玩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)只能在模板内部使用,并且只能在遵循依赖于模板参数的内容的情况下使用。