Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

GCC issue: using a member of a base class that depends on a template argument

The following code doesn't compile with gcc, but does with Visual Studio:

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

I get the error:

test.cpp: In member function ‘void B::bar()’:

test.cpp:11: error: ‘foo’ was not declared in this scope

But it should be! If I change bar to

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

then it does compile, but I don't think I have to do this. Is there something in the official specs of C++ that GCC is following here, or is it just a quirk?

like image 859
Jesse Beder Avatar asked Aug 14 '08 17:08

Jesse Beder


People also ask

When the compiler generates a class function or static data members from a template it is referred to as?

Template Instantiation. When the compiler generates a class, function or static data members from a template, it is referred to as template instantiation. A class generated from a class template is called a generated class.

Can a template base class derived?

It is possible to inherit from a template class. All the usual rules for inheritance and polymorphism apply. If we want the new, derived class to be generic it should also be a template class; and pass its template parameter along to the base class.


2 Answers

David Joyner had the history, here is the reason.

The problem when compiling B<T> is that its base class A<T> is unknown from the compiler, being a template class, so no way for the compiler to know any members from the base class.

Earlier versions did some inference by actually parsing the base template class, but ISO C++ stated that this inference can lead to conflicts where there should not be.

The solution to reference a base class member in a template is to use this (like you did) or specifically name the base class:

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

More information in gcc manual.

like image 82
Vincent Robert Avatar answered Sep 27 '22 20:09

Vincent Robert


Wow. C++ never ceases to surprise me with its weirdness.

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 ();   } }; 

That's just all kinds of crazy. Thanks, David.

Here's the "temp.dep/3" section of the standard [ISO/IEC 14882:2003] that they are referring to:

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>. ]

like image 36
Derek Park Avatar answered Sep 27 '22 19:09

Derek Park