Член шаблона базового класса выходит за рамки в шаблоне производного класса с тем же аргументом шаблона

Следующий код дает мне ошибку компиляции "значение" не было объявлено в этой области.

template<class T>
struct Base {
    int value;
};

template <class T>
struct Derived : public Base<T> {
    int getValue() { return value; }
};

Я считаю крайне странным, что

  • если Derived наследуется от Base<std::string>, компиляция кода,
  • если я return Base<T>::value, код компилируется.

Почему код не компилируется, как есть? Каким образом "значение" не объявлено в области Derived<T>::getValue()?

Ответ 1

Поскольку value является неквалифицированным именем, а во время первого этапа поиска имени компилятор не имеет понятия, что это элемент данных, унаследованный от базового класса (он еще не создал экземпляр Base<T>). Таким образом, он будет искать глобальное пространство имен и не найдет переменной с именем value; следовательно, он выдаст ошибку.

Вот типичный подход для решения этой проблемы:

template <class T>
struct Derived : public Base<T> {
    int getValue() { return this->value; }
    //                      ^^^^^^
};

По правде говоря, разыменование this сообщает компилятору, что последующее имя - это имя (возможно, унаследованного) элемента данных, и поиск должен быть отложен до того момента, когда функция-член фактически создается. Конечно, ваше решение:

return Base<T>::value;

В равной степени хорошо, потому что он также сообщает компилятору, что value наследуется от базового класса Base<T>.

Что касается вывода из Base<std::string>, то компилятор может сразу перейти и посмотреть, содержит ли Base<std::string> элемент данных с именем value (поскольку он не зависит от какого-либо параметра шаблона), и если это так, он сможет определить, что выражение хорошо сформировано.

Однако, если ваш базовый класс Base<T>, где T неизвестен во время первой фазы поиска имени, компилятор не может определить, что value (специализации Base для разных T может даже не иметь value вообще).

Пункт 14.6/3 стандарта С++ 11:

В определении шаблона класса или класса, если базовый класс зависит от шаблона-параметра, базовый класс область не рассматривается при поиске неквалифицированного имени либо в точке определения шаблона класса или член или во время создания шаблона или члена класса. [...] [Пример:

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;

Члены A::B, A::a и A::Y аргумента шаблона A не влияют на привязку имен в Y<A>. -end пример]