4

有人可以解释为什么以下 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 支持编译)。

最后一个版本工作正常,但我不明白为什么使用临时变量来存储引用可以解决问题。

我很想知道为什么其他三个无效。

谢谢

4

2 回答 2

1

在模板内部,表达式可以是类型相关的值相关的。从 14.6.2 开始:

类型和表达式可能取决于模板参数的类型和/或值

在您的情况下,counter是一个模板参数,并且 的声明v依赖于它,从而形成v[0]一个依赖于值的表达式。因此,该名称foo是一个从属名称,您必须通过以下方式消除它作为模板名称的歧义:

v[0].template foo<1>();
于 2012-12-01T16:00:47.953 回答
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.foo1,而是用来将模板参数传递1给函数模板,你需要使用template关键字。

于 2012-12-01T15:55:41.037 回答