GCC问题:使用依赖于模板参数的基类成员

时间:2008-08-14 17:39:47

标签: c++ templates base-class class-members name-lookup

以下代码不使用gcc编译,但使用Visual Studio编译:

template <typename T> class A {
public:
    T foo;
};

template <typename T> class B: public A <T> {
public:
    void bar() { cout << foo << endl; }
};

我收到错误:

  

test.cpp:在成员函数'void B :: bar()'中:

     

test.cpp:11:错误:未在此范围内声明'foo'

但它应该是!如果我将bar更改为

void bar() { cout << this->foo << endl; }

然后它 编译,但我不认为我必须这样做。 GCC在这里遵循C ++官方规范中的某些内容,还是仅仅是一个怪癖?

5 个答案:

答案 0 :(得分:34)

大卫·乔伊纳有历史,这就是原因。

编译B<T>时的问题是编译器不知道它的基类A<T>是模板类,所以编译器无法知道基类中的任何成员。

早期的版本通过实际解析基本模板类做了一些推断,但是ISO C ++声明这种推断可能导致不应该存在的冲突。

在模板中引用基类成员的解决方案是使用this(与您一样)或专门命名基类:

template <typename T> class A {
public:
    T foo;
};

template <typename T> class B: public A <T> {
public:
    void bar() { cout << A<T>::foo << endl; }
};

gcc manual中的更多信息。

答案 1 :(得分:19)

哇。 C ++永远不会因为它的怪异而让我感到惊讶。

  

在模板定义中,非限定名称将不再找到依赖库的成员(由C ++标准中的[temp.dep] / 3指定)。例如,

template <typename T> struct B {
  int m;
  int n;
  int f ();
  int g ();
};
int n;
int g ();
template <typename T> struct C : B<T> {
  void h ()
  {
    m = 0; // error
    f ();  // error
    n = 0; // ::n is modified
    g ();  // ::g is called
  }
};
  

您必须依赖名称,例如通过在它们前面加上这个 - >;以下是C :: h,

的更正定义
template <typename T> void C<T>::h ()
{
  this->m = 0;
  this->f ();
  this->n = 0
  this->g ();
}
  

作为替代解决方案(遗憾的是不向后兼容GCC 3.3),您可以使用声明代替此 - &gt;:

template <typename T> struct C : B<T> {
  using B<T>::m;
  using B<T>::f;
  using B<T>::n;
  using B<T>::g;
  void h ()
  {
    m = 0;
    f ();
    n = 0;
    g ();
  }
};

那只是各种各样的疯狂。谢谢,大卫。

以下是他们所指的标准[ISO / IEC 14882:2003]的“temp.dep / 3”部分:

  

在类模板的定义或类模板的成员中,如果类模板的基类依赖于template-parameter,则在定义的非限定名称查找期间不会检查基类作用域类模板或成员的类或在类模板或成员的实例化期间。 [例如:

typedef double A; 
template<class T> class B { 
    typedef int A; 
}; 
template<class T> struct X : B<T> { 
    A a; // a has typedouble 
}; 
  

A定义中的类型名X<T>绑定到全局命名空间范围中定义的typedef名称,而不是基类B<T>中定义的typedef名称。 ] [例子:

struct A { 
    struct B { /* ... */ }; 
    int a; 
    int Y; 
}; 
int a; 
template<class T> struct Y : T { 
    struct B { /* ... */ }; 
    B b; //The B defined in Y 
    void f(int i) { a = i; } // ::a 
    Y* p; // Y<T> 
}; 
Y<A> ya; 
  

模板参数A::B的成员A::aA::YA不会影响Y<A>中名称的绑定。 ]

答案 2 :(得分:11)

这在gcc-3.4中发生了变化。 C ++解析器在该版本中得到了更严格的规定 - 按照规范,但对于具有传统或多平台代码库的人来说仍然有点烦人。

答案 3 :(得分:8)

C ++在这里不能假设的主要原因是基本模板可以在以后专门用于某种类型。继续原始的例子:

template<>
class A<int> {};

B<int> x; 
x.bar();//this will fail because there is no member foo in A<int>

答案 4 :(得分:3)

VC没有实现两阶段查找,而GCC则实现。因此GCC在实例化之前解析模板,因此发现比VC更多的错误。 在您的示例中,foo是一个依赖名称,因为它取决于'T'。除非你告诉编译器它来自何处,否则在实例化之前它根本无法检查模板的有效性。 这就是为什么你必须告诉编译器它来自哪里。