C ++ ответ (общий ответ)
Рассмотрим класс шаблона 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->
здесь не обязательно.
OTOH, QScopedArrayPointer
является классом шаблона, а d
является унаследованным членом базового класса шаблона:
template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> >
class QScopedArrayPointer : public QScopedPointer<T, Cleanup>
поэтому this->
необходимо здесь :
inline T &operator[](int i)
{
return this->d[i];
}
Легко видеть, что проще просто поставить this->
везде.
Понять причину
Я полагаю, что не всем пользователям C ++ понятно, почему имена ищутся в независимых классах, а не в зависимых базовых классах:
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;
компилятору. Имя не может быть найдено в шаблоне класса! Но только в шаблонной специализации. Шаблон - это фабрика для создания шаблона специализации, шаблон не является набором шаблона специализации . Компилятор может искать d
в Base<T>
для любого конкретного типа T
, но не может
поиск 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>
на самом деле шаблон спецификации базового класса (фабрика для создания спецификаций базового класса), которая следует другим правилам из спецификации базового класса внутри шаблона.
Примечание:
Читатель, возможно, заметил, что я выдумал несколько фраз в конце объяснения.
Это очень отличается: здесь 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>
как известного завершенного класса иногда и как неизвестного класса иногда противоречива, ну, вы правы.