问题
Could someone explain why the following c++ code is not behaving as expected:
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;
}
The code above compiles fine without flag. If one of the flag BUG1 to BUG3 is set, the compilation fails with either GCC 4.6 or 4.7 and with clang 3.2 (which seems to indicate it is not a GCC bug).
Lines 21 to 29 are doing exactly the same thing semantically (ie calling a method of the first element of the Object array), but only the last version compiles. The problem only seems to arise when I try to call a templated method from a template object.
BUG1 is just the "normal" way of writing the call.
BUG2 is the same thing, but the array access is protected by parenthesis in case there was a precedence problem (but there shouldn't be any).
BUG3 shows that type inference is not working either (needs to be compiled with c++11 support).
The last version works fine, but I don't understand why using a temporary variable to store the reference solves the problem.
I am curious to know why the other three are not valid.
Thanks
回答1:
You have to use template as:
v[0].template foo<1>();
auto &o = v[0];
o.template foo<1>();
Because the declaration of v depends on the template argument, which makes v a dependent name.
Here the template keyword tells compiler that whatever follows is a template (in your case, foo is indeed a template). If foo is not a template, then the template keyword is not required (in fact, it would be an error).
The problem is that o.foo<1>() can be parsed/interpreted in two ways: one is just as you expect (a function call), the other way is this:
(o.foo) < 1 //partially parsed
that is, foo is a member data (not function), and you've comparing it with 1. So to tell the compiler that < is not used to compare o.foo with 1, rather it is used to pass template argument 1 to the function template, you're required to use template keyword.
回答2:
Inside templates, expressions can be type-dependent or value-dependent. From 14.6.2:
types and expressions may depend on the type and/or value of template parameters
In your case, counter is a template argument, and the declaration of v depends on it, making v[0] a value-dependent expression. Thus the name foo is a dependent-name which you must disambiguate as a template name by saying:
v[0].template foo<1>();
来源:https://stackoverflow.com/questions/13660958/strange-compilation-behaviour-when-calling-a-template-method-from-another-templa