GCC问题:使用依赖于模板参数的基类成员
时间:2020-03-05 18:39:42 来源:igfitidea点击:
以下代码无法使用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: In member function ‘void B::bar()’: test.cpp:11: error: ‘foo’ was not declared in this scope
但这应该是!如果我将bar
更改为
void bar() { cout << this->foo << endl; }
然后它会编译,但我认为我不必这样做。 GCC遵循的C ++正式规范中是否有某些内容,还是只是一个怪癖?
解决方案
回答
这在gcc-3.4中已更改。按照规范,C ++解析器在该发行版中变得更加严格,但对于具有旧版或者多平台代码库的人员而言仍然有些烦人。
回答
哇。 C ++的怪异永远不会令我惊讶。
In a template definition, unqualified names will no longer find members of a dependent base (as specified by [temp.dep]/3 in the C++ standard). For example,
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 } };
You must make the names dependent, e.g. by prefixing them with this->. Here is the corrected definition of C::h,
template <typename T> void C<T>::h () { this->m = 0; this->f (); this->n = 0 this->g (); }
As an alternative solution (unfortunately not backwards compatible with GCC 3.3), you may use using declarations instead of 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"部分:
In the definition of a class template or a member of a class template, if a base class of the class template depends on a template-parameter, the base class scope is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member. [Example:
typedef double A; template<class T> class B { typedef int A; }; template<class T> struct X : B<T> { A a; // a has typedouble };
The type name A in the definition of X<T> binds to the typedef name defined in the global namespace scope, not to the typedef name defined in the base class B<T>. ] [Example:
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;
The members A::B, A::a, and A::Y of the template argument A do not affect the binding of names in Y<A>. ]
回答
大卫·乔伊纳(David Joyner)拥有历史,这就是原因。
编译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手册中有更多信息。
回答
C ++在这里不能做任何事情的主要原因是基本模板以后可以专门用于类型。继续原始示例:
template<> class A<int> {}; B<int> x; x.bar();//this will fail because there is no member foo in A<int>
回答
VC没有实现两阶段查找,而GCC却实现了两阶段查找。因此,GCC在实例化模板之前先对其进行解析,从而发现比VC更多的错误。
在示例中,foo是从属名称,因为它取决于'T'。除非我们告诉编译器其来源,否则在实例化模板之前,它根本无法检查模板的有效性。
这就是为什么我们必须告诉编译器它的来源。