6

So, say you have a base class which is recursive (e.g. a linked list) and a derived class as well. The derived class should reuse the constructor from the base class, because you don't want to write redundant code. You could try the obvious thing, and it won't work:

class Base {
public:

     Base(int size) {
      if (size <= 0) { next = NULL; }
      else { next = new Base(size - 1); }
     }
     void print() { 
      cout << " Base  ";
      if (next != NULL) { next->print(); }
     }
protected:
     Base *next;
};
class Derived: public Base {
public:
     Derived(int size) : Base(size) {} 
     void print() 
      { 
           cout << " Derived ";
           if (next != NULL) 
           { next->print(); }
      }

};
int main()
{

     Derived d2(5);
     d2.print();
     cout << "\n";
     return 0;
}

This won't work - when you instantiate Derived, it constructs one Derived instance, then calls the Base class constructor which pumps out Base instance after Base instance. If you run "main" you'll get:

Derived  Base   Base   Base   Base   Base 

Now, you can get smart and use something like the following design pattern: http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern which will solve all your problems. Check out the following really neat code:

template <class targetT, class recursiveT>
class Base {
public:

     Base(targetT size) {
      if (size <= 0) { next = NULL; }
      else { next = new recursiveT(size - 1); }
     }

     void print() { 
      cout << " Base  ";
      if (next != NULL) 
      { next->print(); }
     }

protected:
     recursiveT *next;
};
class Derived: public Base<int, Derived> {
public:
     Derived(int size) : Base<int, Derived>(size) {} 
     void print() 
      { 
           cout << " Derived ";
           if (next != NULL) 
           { next->print(); }
      }
};
int main()
{

     Derived d1(5);
     d1.print();
     cout << "\n";
     return 0;
}

This passes the test - when we punt from the constructor of Derived back to the constructor of Base, templating causes Base to pump out instances of Derived. Neat! If you run main you'll see the following:

 Derived  Derived  Derived  Derived  Derived  Derived 

Just like you wanted!

Now things get strange. Say that we wanted Base and Derived to be templated themselves; say they are linked lists and we want them to hold arbitrary data.

So we can push this a little further:

template <class targetT, class recursiveT>
class Base {
public:

     Base(targetT size) {
      if (size <= 0) { next = NULL; }
      else { next = new recursiveT(size - 1); }
     }

     void print() { 
      cout << " Base  ";
      if (next != NULL) 
      { next->print(); }
     }

protected:
     recursiveT *next;
};
template <class T>
class Derived: public Base<T, Derived<T> > {
public:
     Derived(int size) : Base<T, Derived<T> >(size) {} 
     void print() 
      { 
           cout << " Derived ";
           if (next != NULL) 
           { next->print(); }
      }
}; 
int main()
{

     Derived<int> d1(5);
     d1.print();
     cout << "\n";
     return 0;
}

But amazingly, compilation now fails with g++:

X.cpp: In member function ‘void Derived<T>::print()’:
X.cpp:33: error: ‘next’ was not declared in this scope

Does anyone see why this should be the case? I'm almost suspecting g++ is wrong, here. I have this problem with gcc version 4.3.2 and gcc version 4.4.1.

user511493
  • 263
  • 1
  • 2
  • 7
  • Whoop, I found a solution, but I don't understand why it worked: in Derived, I replaced the `next`'s with `this->next`'s. – user511493 Feb 12 '11 at 01:26
  • possible duplicate of [Why doesn't a derived template class have access to a base template class' identifiers?](http://stackoverflow.com/questions/1239908/why-doesnt-a-derived-template-class-have-access-to-a-base-template-class-identi) – James McNellis Feb 12 '11 at 01:33

1 Answers1

12

The problem is that the base class, Base<T, Derived<T> >, is a dependent base class because it depends on the template parameters, but next is not a dependent name because it doesn't depend on the template parameters. The compiler does not look up nondependent names in dependent base classes.

You can fix this problem by making next dependent by accessing it via this->:

this->next

You can find out more from the C++ FAQ Lite article, "Why am I getting errors when my template-derived-class uses a member it inherits from its template-base-class?"

James McNellis
  • 348,265
  • 75
  • 913
  • 977
  • Wow, great answer and great FAQ link. So new question: Why would C++ do things this way? It seems like such an arbitrary restriction! – user511493 Feb 12 '11 at 01:59
  • @user511493: The restriction is not arbitrary at all. The limitation is there because the compiler doesn't know that `next` is a data member of the inherited class `Base<>`. It can't know for sure because `Base<>` might be specialized for certain `T`, meaning that `next` could be something that's not a data member. Because of this, the C++ standard requires that the compiler issue a diagnostic. – In silico Feb 12 '11 at 02:29
  • @In: Well, that just begs the question: why does C++ mandate two-phase name lookup? Why isn't all name lookup deferred until instantiation? I honestly don't know the answer to that question off the top of my head (I suppose I could go open up _C++ Templates: The Complete Guide_ or hunt down litb in the C++ Lounge chat room and find out), and the compiler I use on a day to day basis (Visual C++) does not actually support two-phase name lookup, so the code works fine with or without making the name dependent. – James McNellis Feb 12 '11 at 02:35
  • @James McNellis: I suspect it's so that errors in the template itself can be caught by the compiler as early as possible. In other words, errors involving non-dependent names can be found by the compiler while parsing the template class, rather than the point of instantiation. It's obviously impossible to do that kind of checking on dependent names, so those are deferred until all template arguments are known. – In silico Feb 12 '11 at 02:53
  • @James The programmer of the template gets the guarantee that some parts of the template code will not be affected by the template parameters. Basically, the programmer can choose which parts are affected and which parts are not (e.g. suppose the programmer intended to use the next() function from the current namespace, and suddenly the T::next() is taken because T defined one!). A second benefit is that the compiler is able to check the non-dependent part of the template without instantiating it. – Sjoerd Feb 12 '11 at 02:59
  • 2
    @James: two-phases look-up is mandated to minimize surprises. Sjoerd already brushed the subject with free-function `next` vs member-function `next`, but there is more. Look-up of non-dependent names occurring at the point of declaration (not instantiation) they should not be affected by whatever is declared afterward (that the library writer cannot know about). Which is another reason not to include *Your* headers before a *Library* headers. VC++ is unfortunately lacking in this regard, and it can lead to subtle gotchas when migrating from a standard compliant compiler. – Matthieu M. Feb 12 '11 at 10:31