Доступ к защищенным членам суперкласса в С++ с помощью шаблонов
Почему компилятор С++ не может распознать, что 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
общедоступными в суперклассе, он все равно терпит неудачу с той же ошибкой.
Ответы
Ответ 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
). Более явные формы просто говорят "доверься мне - это должно происходить из базового класса во время создания экземпляра (иначе будет ошибка компилятора)".