我一直玩clang一段时间,我偶然发现了“test / SemaTemplate / dependent-template-recover.cpp”(在clang发行版中),它应该提供从模板错误中恢复的提示。 / p>
整个事情很容易被剥离到一个最小的例子:
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>();
}
};
clang产生的错误消息:
tpl.cpp:6:13: error: use 'template' keyword to treat 'f0' as a dependent template name
t->f0<U>();
^
template
1 error generated.
...但我很难理解究竟应该插入template
关键字以使代码在语法上正确无误?
答案 0 :(得分:77)
ISO C ++ 03 14.2 / 4:
当成员模板专业化的名称出现之后。或 - &gt;在postfix-expression中,或者在qualified-id中的nested-name-specifier之后,postfix-expression或qualified-id显式依赖于template-parameter(14.6.2),成员模板名称必须是前缀为关键字模板。否则,假定该名称命名非模板。
在t->f0<U>();
f0<U>
中是一个成员模板专精,它出现在->
之后,并明确依赖于模板参数U
,因此成员模板专业化必须以{为前缀} {1}}关键字。
所以将template
更改为t->f0<U>()
。
答案 1 :(得分:22)
除了其他人提出的要点之外,请注意有时编译器无法下定决心,并且两种解释都可以在实例化时产生其他有效的程序
#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*)a.f<int()>(0);
}
int main() {
std::cout << g<void>() << std::endl;
}
在0
之前省略template
但在插入f<int()>
时省略1
时会打印{{1}}。我把它留作练习来弄清代码的作用。
答案 2 :(得分:9)
在插入符号的位置之前插入它:
template<typename T, typename U, int N> struct X {
void f(T* t)
{
t->template f0<U>();
}
};
编辑:如果您像编译器一样思考,此规则的原因会变得更清晰。 编译器通常只会一次向前看一个或两个令牌,并且通常不会“向前看”表达式的其余部分。 [编辑:请参阅注释]关键字的原因与为什么你需要typename
关键字来表示依赖类型名称:它告诉编译器“嘿,您将要看到的标识符是模板的名称,而不是静态数据成员的名称,后跟一个低于标志“。
答案 3 :(得分:7)
.template构造 引入typename后发现了一个非常类似的问题。请考虑以下使用标准bitset类型的示例:
template<int N>
void printBitset (std::bitset<N> const& bs)
{
std::cout << bs.template to_string<char,char_traits<char>,
allocator<char> >();
}
此示例中的奇怪构造是.template。如果没有额外使用模板,编译器就不会知道后面的小于令牌(&lt;)并不是真正“小于”,而是模板参数列表的开头。请注意,仅当句点之前的构造取决于模板参数时,才会出现此问题。在我们的示例中,参数bs取决于模板参数N。
总之,.template符号(以及类似符号,例如 - &gt;模板)只能在模板内部使用,并且只有在它们遵循依赖于模板参数的内容时才会使用。