令人困惑的模板错误
Posted
技术标签:
【中文标题】令人困惑的模板错误【英文标题】:Confusing Template error 【发布时间】:2011-04-16 17:27:24 【问题描述】:我一直在玩 clang,我偶然发现了“test/SemaTemplate/dependent-template-recover.cpp”(在 clang 发行版中),它应该提供从模板错误中恢复的提示。
整个事情可以很容易地简化为一个最小的例子:
template<typename T, typename U, int N> struct X
void f(T* t)
// expected-erroruse '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
关键字以使代码在语法上正确?
【问题讨论】:
您是否尝试将其插入箭头指向的位置? 类似于this和this 【参考方案1】:ISO C++03 14.2/4:
当成员模板特化的名称出现在 .或 -> 在后缀表达式中,或在限定 ID 中的嵌套名称说明符之后,并且后缀表达式或限定 ID 显式依赖于模板参数 (14.6.2),成员模板名称必须以关键字模板为前缀。否则,该名称被假定为命名一个非模板。
在t->f0<U>();
中f0<U>
是出现在->
之后的成员模板特化,它显式依赖于模板参数U
,因此成员模板特化必须以template
关键字为前缀。
所以将t->f0<U>()
更改为t->template f0<U>()
。
【讨论】:
有趣的是,我认为将表达式放在括号中:t->(f0<U>())
会解决这个问题,因为我认为这会将 f0<U>()
放入独立表达式中......好吧,我想错了,看来。 ..
您能否评论一下为什么会这样?为什么 C++ 需要这种语法?
是的,这很奇怪。该语言可以“检测”模板关键字需要存在。如果它可以做到这一点,那么它应该只是在其中“插入”关键字。【参考方案2】:
除了其他人提出的观点之外,请注意有时编译器无法下定决心,并且两种解释都可以在实例化时产生替代的有效程序
#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;
当在f<int()>
之前省略template
时会打印0
,但在插入时会打印1
。我把它作为一个练习来弄清楚代码的作用。
【讨论】:
现在这是一个可怕的例子! 我无法重现您在 Visual Studio 2013 中描述的行为。它始终调用f<U>
并始终打印 1
,这对我来说非常有意义。我仍然不明白为什么需要 template
关键字以及它有什么区别。
@Violet VSC++ 编译器不是兼容的 C++ 编译器。如果您想知道为什么 VSC++ 总是打印 1,则需要一个新问题。
这个答案解释了为什么需要template
:***.com/questions/610245/…,而不是仅仅依赖于难以理解的标准术语。如果该答案中的任何内容仍然令人困惑,请报告。
@JohannesSchaub-litb:谢谢,一个很好的答案。原来,我以前读过它,因为它已经被我点赞了。显然,我的记忆力很好。【参考方案3】:
将它插入到插入符号所在的点之前:
template<typename T, typename U, int N> struct X
void f(T* t)
t->template f0<U>();
;
编辑:如果你像编译器一样思考,这条规则的原因会变得更清楚。 编译器一般一次只向前看一两个标记,一般不会“向前看”到表达式的其余部分。[编辑:见评论]关键字的原因与为什么需要 typename
关键字来指示依赖类型名称:它告诉编译器“嘿,您将要看到的标识符是模板的名称,而不是静态数据成员的名称,后跟一个 less-比签名”。
【讨论】:
我本来从来没有能够猜到...但是谢谢你;-)。显然,C++ 总有一些东西要学! 即使无限前瞻,您仍然需要template
。在某些情况下,无论有没有template
,都会产生具有不同行为的有效程序。所以这不仅仅是一个语法问题(t->f0<int()>(0)
在语法上对于小于和模板参数列表版本都是有效的)。
@Johannes Schaub - litb:是的,所以与向前看相比,为表达分配一致的语义意义更多的是问题。【参考方案4】:
摘自C++ Templates
.template 构造 在引入 typename 之后发现了一个非常相似的问题。考虑以下使用标准位集类型的示例:
template<int N>
void printBitset (std::bitset<N> const& bs)
std::cout << bs.template to_string<char,char_traits<char>,
allocator<char> >();
这个例子中奇怪的结构是 .template。如果没有额外使用模板,编译器不知道后面的小于标记 (
总之,.template 表示法(以及类似的表示法,例如 ->template)应该只在模板内部使用,并且只有在它们遵循依赖于模板参数的内容时才应使用。
【讨论】:
以上是关于令人困惑的模板错误的主要内容,如果未能解决你的问题,请参考以下文章