Strange compilation behavior when calling a template method from another template object

Can anyone explain why the following C ++ code does not work 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 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 without a flag. If one of the BUG1 flags is set to BUG3, compilation fails with GCC 4.6 or 4.7 and with clang 3.2 (which seems to indicate that this is not a GCC error).

Lines 21 through 29 do the same thing semantically (i.e., calling the method of the first element of the Object array), but only the latest version is compiled. The problem only occurs when trying to call a template method from a template object.

BUG1 is just a โ€œnormalโ€ way to record a call.

BUG2 is the same thing, but access to the array is protected by brackets in case there is a problem with priority (but they should not be).

BUG3 shows that type inference also does not work (it must be compiled with C ++ 11 support).

The latest version works fine, but I donโ€™t understand why using a temporary variable to store help solves the problem.

I am curious to know why the other three are invalid.

thanks

+4
source share
2 answers

You should use template like:

 v[0].template foo<1>(); auto &o = v[0]; o.template foo<1>(); 

Since the declaration of v depends on the template argument, which makes v dependent name.

Here the template keyword tells the compiler that everything that follows it is a template (in your case, foo really is a template). If foo not a template, the template keyword is not required (in fact it will be an error).

The problem is that o.foo<1>() can be parsed / interpreted in two ways: one just as you expect (function call), otherwise:

 (o.foo) < 1 //partially parsed 

i.e. foo is the member data (not a function) and you compare it to 1 . Therefore, to tell the compiler that < not used to compare o.foo with 1 , rather, it is used to pass the argument of template 1 to the function template, you need to use the template keyword.

+1
source

Within templates, expressions can be type dependent or cost dependent. From 14.6.2:

types and expressions may depend on the type and / or value of the template parameters

In your case, counter is the template argument, and the declaration of v depends on it, making v[0] value-dependent expression. Thus, the name foo is a dependent name, which you must eliminate as the template name by specifying:

 v[0].template foo<1>(); 
+1
source

All Articles