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

下面的代码不能用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; } 

那么它编译,但我不认为我必须这样做。 在C ++的官方规范中是否有这样的东西,GCC在这里,还是只是一个怪癖?

这在gcc-3.4中改变了。 C ++parsing器在该版本中得到了更严格的要求 – 按照规范,但对于具有遗留或多平台代码库的人员来说,仍然有些烦人。

大卫Joyner有历史,这是原因。

编译B<T>是,它的基类A<T>在编译器中是未知的,它是一个模板类,因此编译器无法知道基类中的任何成员。

早期版本通过实际parsing基本模板类来进行一些推理,但ISO C ++声明,这种推断可能会导致不应有的冲突。

在模板中引用基类成员的解决scheme是使用(像你一样)或专门命名基类:

 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手册 。

哇。 奇怪的是,C ++永远不会让我感到惊讶。

在模板定义中,非限定名称将不再find从属基础的成员(在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 } }; 

你必须使名字依赖,例如用this->作为前缀。 这里是C :: h的校正定义,

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

作为另一种解决scheme(不幸的是不能向后兼容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 (); } }; 

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

这是他们所指的标准[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 }; 

X<T>定义中的types名称A绑定到全局名称空间作用域中定义的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的成员A::BA::aA::Y不影响Y<A>中名称的绑定。 ]

C ++在这里不能采取任何事情的主要原因是基模板以后可以专用于某种types。 继续原来的例子:

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

VC没有实现两阶段查找,而GCC则是。 所以GCC在实例化之前parsing模板,从而发现比VC更多的错误。 在你的例子中,foo是一个独立的名字,因为它依赖于'T'。 除非你告诉编译器它来自哪里,否则它在实例化之前根本不能检查模板的有效性。 这就是为什么你必须告诉编译器它来自哪里。