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

亡梦爱人 提交于 2019-12-10 18:08:52

问题


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

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!