有人可以解释为什么以下c ++代码的行为不符合预期:
struct Object {
template< int i >
void foo(){ }
};
template<int counter>
struct Container {
Object v[counter];
void test(){
// this works as expected
Object a; a.foo<1>();
// This works as well:
Object *b = new Object(); b->foo<1>();
// now try the same thing with the array:
v[0] = Object(); // that's fine (just testing access to the array)
# if defined BUG1
v[0].foo<1>(); // compilation fails
# elif defined BUG2
(v[0]).foo<1>(); // compilation fails
# elif defined BUG3
auto &o = v[0];
o.foo<1>(); // compilation fails
# else
Object &o = v[0];
o.foo<1>(); // works
# endif
}
};
int main(){
Container<10> container;
}
上面的代码编译好没有标志。如果设置了BUG1到BUG3标志之一,则编译将失败,使用GCC 4.6或4.7以及使用clang 3.2(这似乎表明它不是GCC错误)。
第21到29行在语义上完全相同(即调用Object数组的第一个元素的方法),但只编译最后一个版本。当我尝试从模板对象调用模板化方法时,似乎只会出现这个问题。
BUG1只是编写通话的“正常”方式。
BUG2是一回事,但是如果存在优先级问题(但不应该有任何问题),则数组访问受括号保护。
BUG3表明类型推断也不起作用(需要使用c ++ 11支持进行编译)。
最后一个版本运行正常,但我不明白为什么使用临时变量存储引用可以解决问题。
我很想知道为什么其他三个无效。
由于
答案 0 :(得分:1)
您必须使用template
作为:
v[0].template foo<1>();
auto &o = v[0];
o.template foo<1>();
因为v
的声明取决于模板参数,这使v
成为从属名称。
此处template
关键字告诉编译器,后面的任何内容都是模板(在您的情况下,foo
确实是模板)。如果foo
不是模板,则不需要template
关键字(事实上,这将是一个错误)。
问题是o.foo<1>()
可以用两种方式解析/解释:一种是你期望的(函数调用),另一种方式是:
(o.foo) < 1 //partially parsed
即,foo
是成员数据(不是函数),您将其与1
进行比较。因此,告诉编译器<
不用于将o.foo
与1
进行比较,而是用于将模板参数1
传递给函数模板,您需要使用template
关键字。
答案 1 :(得分:1)
在内部模板中,表达式可以是类型相关的或依赖于值的。从14.6.2开始:
类型和表达式可能取决于模板参数的类型和/或值
在您的情况下,counter
是模板参数,v
的声明取决于它,使v[0]
成为依赖于值的表达式。因此,名称foo
是一个依赖名称,您必须通过以下方式消除模板名称的歧义:
v[0].template foo<1>();