Ответ 1
Ответ на С++ (общий ответ)
Рассмотрим шаблонный класс Derived
с базовым классом шаблона:
template <typename T>
class Base {
public:
int d;
};
template <typename T>
class Derived : public Base<T> {
void f () {
this->d = 0;
}
};
this
имеет тип Derived<T>
, тип которого зависит от T
. Итак, this
имеет зависимый тип. Итак, this->d
делает d
зависимым именем. Зависимые имена просматриваются в контексте определения шаблона как не зависящие имена и в контексте создания экземпляра.
Без this->
имя d
будет отображаться только как необязательное имя и не будет найдено.
Другим решением является объявление d
в самом определении шаблона:
template <typename T>
class Derived : public Base<T> {
using Base::d;
void f () {
d = 0;
}
};
Qanswer (конкретный ответ)
d
является членом QScopedPointer
. Это не унаследованный член. this->
здесь не требуется.
template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> >
class QScopedArrayPointer : public QScopedPointer<T, Cleanup>
поэтому this->
необходимо здесь:
inline T &operator[](int i)
{
return this->d[i];
}
Легко видеть, что проще просто поместить this->
всюду.
Понять причину
Я думаю, всем пользователям на С++ не ясно, почему имена ищутся в независящих базовых классах, но не в зависимых базовых классах:
class Base0 {
public:
int nd;
};
template <typename T>
class Derived2 :
public Base0, // non-dependent base
public Base<T> { // dependent base
void f () {
nd; // Base0::b
d; // lookup of "d" finds nothing
f (this); // lookup of "f" finds nothing
// will find "f" later
}
};
Есть причина, почему "стандарт говорит так": причина использования привязки имени в шаблонах.
Шаблоны могут иметь имя, которое связано с поздним, когда экземпляр шаблона создается: например f
в f (this)
. В точке определения Derived2::f()
нет переменной, функции или имени типа f
, известных компилятору. Множество известных объектов, которые f
может ссылаться на это, пусто в этой точке. Это не проблема, потому что компилятор знает, что он будет искать f
позже как имя функции или имя функции шаблона.
OTOH, компилятор не знает, что делать с d
; это не имя функции. Невозможно выполнить позднюю привязку к именам функций без имени.
Теперь все это может показаться элементарным знанием полиморфизма шаблонов времени компиляции. Кажется, что возникает реальный вопрос: почему не d
связано с Base<T>::d
во время определения шаблона?
Реальная проблема заключается в том, что во время определения шаблона нет Base<T>::d
, потому что в это время нет полного типа Base<T>
: Base<T>
объявлен, но не определен!. может спросить: как насчет этого:
template <typename T>
class Base {
public:
int d;
};
это выглядит как определение полного типа!
Собственно, до создания экземпляра это больше похоже на:
template <typename T>
class Base;
для компилятора. Имя не может быть просмотрено в шаблоне класса! Но только в шаблонной специализации (экземпляре). Шаблон представляет собой factory, чтобы сделать специализацию шаблона, шаблон не является набором специализированных шаблонов. Компилятор может искать d
в Base<T>
для любого конкретного типа T
, но он не может
lookup d
в шаблоне класса Base
. Пока не будет определен тип T
, Base<T>::d
остается абстрактным Base<T>::d
; только когда тип T
известен, Base<T>::d
начинает ссылаться на переменную типа int
.
Следствием этого является то, что шаблон класса Derived2
имеет полный базовый класс Base0
, но неполный (объявленный вперед) базовый класс Base
. Только для известного типа T
, "класс шаблона" (специализации шаблона класса) Derived2<T>
имеет полные базовые классы, как и любой нормальный класс.
Теперь вы увидите, что:
template <typename T>
class Derived : public Base<T>
на самом деле является базовым классом спецификации (a factory для спецификации базового класса), который следует за разными правилами из спецификации базового класса внутри шаблона.
Примечание: Возможно, читатель заметил, что в конце объяснения я составил несколько фраз.
Это совсем другое: здесь d
является квалифицированным именем в Derived<T>
, а Derived<T>
зависит от T
является параметром шаблона. Квалифицированное имя может быть связано с поздним сроком, даже если оно не является именем функции (называемой).
Еще одно решение:
template <typename T>
class Derived : public Base<T> {
void f () {
Derived::d = 0; // qualified name
}
};
Это эквивалентно.
Если вы считаете, что внутри определения Derived<T>
обработка Derived<T>
как известного полного класса иногда и как неизвестный класс несколько раз непоследовательна, ну, вы правы.