C ++: шаблонные унаследованные рекурсивные классы: невозможная тройная угроза? - PullRequest
6 голосов
/ 12 февраля 2011

Итак, скажем, у вас есть базовый класс, который является рекурсивным (например, связанный список), а также производный класс.Производный класс должен повторно использовать конструктор из базового класса, потому что вы не хотите писать избыточный код.Вы можете попробовать очевидную вещь, и она не будет работать:

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

Это не будет работать - когда вы создаете экземпляр Derived, он создает один экземпляр Derived, а затем вызывает конструктор класса Base, который выкачивает Baseэкземпляр после базового экземпляра.Если вы запустите «main», вы получите:

Derived  Base   Base   Base   Base   Base 

Теперь вы можете стать умнее и использовать что-то вроде следующего шаблона проектирования: http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern, который решит все ваши проблемы.Посмотрите на следующий действительно аккуратный код:

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

Это проходит тест - когда мы возвращаемся из конструктора Derived обратно в конструктор Base, шаблоны заставляют Base откачивать экземпляры Derived.Ухоженная!Если вы запустите main, вы увидите следующее:

 Derived  Derived  Derived  Derived  Derived  Derived 

Как вы и хотели!

Теперь все становится странным.Скажем, мы хотели, чтобы Base и Derived сами были шаблонами;скажем, что это связанные списки, и мы хотим, чтобы они содержали произвольные данные.

Итак, мы можем продвинуться немного дальше:

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

Но, что удивительно, компиляция теперь не удалась с g ++:

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

Кто-нибудь видит, почему так должно быть?Я почти подозреваю, что G ++ не так, здесь.У меня есть эта проблема с gcc версии 4.3.2 и gcc версии 4.4.1.

1 Ответ

12 голосов
/ 12 февраля 2011

Проблема в том, что базовый класс Base<T, Derived<T> > является зависимым базовым классом, поскольку он зависит от параметров шаблона, но next не является зависимым именем, поскольку он не зависит от параметров шаблона.Компилятор не ищет независимые имена в зависимых базовых классах.

Вы можете решить эту проблему, установив next зависимым, обратившись к нему через this->:

this->next

Вы можете найтибольше из статьи C ++ FAQ Lite «Почему я получаю ошибки, когда мой производный от шаблона класс использует член, который он наследует от своего базового класса шаблона?»

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...