C++ answer (general answer)
Consider a template class Derived
with a template base class:
template <typename T>
class Base {
public:
int d;
};
template <typename T>
class Derived : public Base<T> {
void f () {
this->d = 0;
}
};
this
has type Derived<T>
, a type which depends on T
. So this
has a dependent type. So this->d
makes d
a dependent name. Dependent names are looked-up in the context of the template definition as non-dependent names and in the context of instantiation.
Without this->
, the name d
would only be looked-up as a non-dependent name, and not be found.
Another solution is to declare d
in the template definition itself:
template <typename T>
class Derived : public Base<T> {
using Base::d;
void f () {
d = 0;
}
};
Qanswer (specific answer)
d
is a member of QScopedPointer
. It isn't an inherited member. this->
is not necessary here.
OTOH, QScopedArrayPointer
is a template class and d
is an inherited member of a template base class:
template <typename T, typename Cleanup = QScopedPointerArrayDeleter<T> >
class QScopedArrayPointer : public QScopedPointer<T, Cleanup>
so this->
is necessary here:
inline T &operator[](int i)
{
return this->d[i];
}
It's easy to see that it's easier to just put this->
everywhere.
Understand the reason
I guess it isn't clear to all C++ users why names are looked-up in non-dependent base classes but not in dependent base classes:
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
}
};
There is a reason beside "the standard says so": cause of way name binding in templates works.
Templates can have name that are bound late, when the template is instantiated: for example f
in f (this)
. At the point of Derived2::f()
definition, there is no variable, function or type name f
known by the compiler. The set of known entities that f
could refer to is empty at this point. This isn't a problem because the compiler knows it will lookup f
later as a function name, or a template function name.
OTOH, the compiler doesn't know what to do with d
; it isn't a (called) function name. There is no way to do late binding on non-(called) functions names.
Now, all of this may seem like elementary knowledge of compile-time template polymorphism. The real question seems to be: why isn't d
bound to Base<T>::d
at template definition time?
The real issue is that there is no Base<T>::d
at template definition time, because there is no complete type Base<T>
at that time: Base<T>
is declared, but not defined! You may ask: what about this:
template <typename T>
class Base {
public:
int d;
};
it looks like the definition of a complete type!
Actually, until instantiation, it looks more like:
template <typename T>
class Base;
to the compiler. A name cannot be looked-up in a class template! But only in a template specialisation (instantiation). The template is a factory to make template specialisation, a template isn't a set of template specialisation. The compiler can lookup d
in Base<T>
for any particular type T
, but it cannot
lookup d
in the class template Base
. Until a type T
is determined, Base<T>::d
remains the abstract Base<T>::d
; only when type T
is known, Base<T>::d
start to refer to a variable of type int
.
The consequence of this is that the class template Derived2
has a complete base class Base0
but an incomplete (forward declared) base class Base
. Only for a known type T
, the "template class" (specialisations of a class template) Derived2<T>
has a complete base classes, just like any normal class.
You now see that:
template <typename T>
class Derived : public Base<T>
is actually a base class specification template (a factory to make base class specifications) that follows different rules from a base class specification inside a template.
Remark:
The reader may have noticed that I have made-up a few phrases at the end of the explanation.
This is very different: here d
is a qualified name in Derived<T>
, and Derived<T>
is dependent since T
is a template parameter. A qualified name can be late-bound even if it isn't a (called) function name.
Yet another solution is:
template <typename T>
class Derived : public Base<T> {
void f () {
Derived::d = 0; // qualified name
}
};
This is equivalent.
If you think that inside the definition of Derived<T>
, the treatment of Derived<T>
as a known complete class sometimes and as an unknown class some other times in inconsistent, well, you are right.