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

Jes*_*der 34 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; }
};
Run Code Online (Sandbox Code Playgroud)

我收到错误:

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

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

但它应该是!如果我换bar

void bar() { cout << this->foo << endl; }
Run Code Online (Sandbox Code Playgroud)

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

Vin*_*ert 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; }
};
Run Code Online (Sandbox Code Playgroud)

有关gcc手册的更多信息.

  • 一方面,这种做法是有道理的.但另一方面,感觉真的很蹩脚.在模板实例化之前,编译器不需要*知道`foo`引用了什么,此时它应该能够识别`A`中的`foo`成员.C++有太多这些奇怪的角落案例. (8认同)

Der*_*ark 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
  }
};
Run Code Online (Sandbox Code Playgroud)

您必须使名称依赖,例如通过在其前面加上this->.这是C :: h的更正定义,

template <typename T> void C<T>::h ()
{
  this->m = 0;
  this->f ();
  this->n = 0
  this->g ();
}
Run Code Online (Sandbox Code Playgroud)

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

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 ();
  }
};
Run Code Online (Sandbox Code Playgroud)

这只是各种疯狂.谢谢,大卫.

这是他们所指的标准[ISO/IEC 14882:2003]的"temp.dep/3"部分:

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

typedef double A; 
template<class T> class B { 
    typedef int A; 
}; 
template<class T> struct X : B<T> { 
    A a; // a has typedouble 
}; 
Run Code Online (Sandbox Code Playgroud)

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

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; 
Run Code Online (Sandbox Code Playgroud)

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


Dav*_*ner 11

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


Mat*_*ice 8

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

template<>
class A<int> {};

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