Подтвердить что ты не робот

Доступ к защищенным членам суперкласса в С++ с помощью шаблонов

Почему компилятор С++ не может распознать, что g() и b являются наследуемыми членами Superclass, как показано в этом коде:

template<typename T> struct Superclass {
 protected:
  int b;
  void g() {}
};

template<typename T> struct Subclass : public Superclass<T> {
  void f() {
    g(); // compiler error: uncategorized
    b = 3; // compiler error: unrecognized
  }
};

Если я упрощаю Subclass и просто наследую от Subclass<int>, тогда он компилируется. Он также компилируется при полной квалификации g() как Superclass<T>::g() и Superclass<T>::b. Я использую LLVM GCC 4.2.

Примечание. Если я делаю g() и b общедоступными в суперклассе, он все равно терпит неудачу с той же ошибкой.

4b9b3361

Ответ 1

Это можно изменить, потянув имена в текущую область с помощью using:

template<typename T> struct Subclass : public Superclass<T> {
  using Superclass<T>::b;
  using Superclass<T>::g;

  void f() {
    g();
    b = 3;
  }
};

Или путем присвоения имени с помощью доступа к указателю this:

template<typename T> struct Subclass : public Superclass<T> {
  void f() {
    this->g();
    this->b = 3;
  }
};

Или, как вы уже заметили, присвоив полное имя.

Причина, по которой это необходимо, заключается в том, что С++ не рассматривает шаблоны суперкласса для разрешения имен (потому что тогда они являются зависимыми именами и зависимыми именами не рассматриваются). Он работает, когда вы используете Superclass<int>, потому что это не шаблон (его экземпляр шаблона), и поэтому его вложенные имена не являются зависимыми именами.

Ответ 2

Ответ Конрада не спрашивает и не отвечает на окончательное "почему" во всем этом. Это не просто комитет С++, произвольно говорящий "эй, откажитесь от зависимых имен, им все равно никто не любит". Скорее, компилятор делает некоторую проверку шаблонов еще до того, как они были созданы, и он не может воспринимать g() или b, пока не узнает T, поскольку он не может - в общем случае - выбирать между возможными специализациями базовый класс (например, SuperClass<X> может иметь int b, а SuperClass<Y> имеет void b() и SuperClass<Z> вообще не имеет b). Более явные формы просто говорят "доверься мне - это должно происходить из базового класса во время создания экземпляра (иначе будет ошибка компилятора)".