Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

In a templated derived class, why do I need to qualify base class member names with "this->" inside a member function?

While I investigate source code of Qt I saw that trolltech guys explicitly use this keyword to access a field on destructor.

inline ~QScopedPointer() {     T *oldD = this->d;     Cleanup::cleanup(oldD);     this->d = 0; } 

So, what's the point of this usage? Are there any benefits?

Edit: For those who vote for closing this question, I suspect that this usage is for some class inheritance cases

A part of QScopedPointer class definition:

template <typename T, typename Cleanup = QScopedPointerDeleter<T> > class QScopedPointer 
like image 616
useraged Avatar asked Oct 26 '11 20:10

useraged


People also ask

How do you access members of the base class from within a derived class?

You can use both a structure and a class as base classes in the base list of a derived class declaration: If the derived class is declared with the keyword class , the default access specifier in its base list specifiers is private .

How do you identify a base class and a derived class?

1. A base class is an existing class from which the other classes are derived and inherit the methods and properties. A derived class is a class that is constructed from a base class or an existing class.

What does a derived class inherit from the base class?

The derived class inherits all members and member functions of a base class. The derived class can have more functionality with respect to the Base class and can easily access the Base class. A Derived class is also called a child class or subclass.

Can a template class be derived from a non template class?

Deriving from a non-template base classIt is quite possible to have a template class inherit from a 'normal' class. This mechanism is recommended if your template class has a lot of non-template attributes and operations. Instead of putting them in the template class, put them into a non-template base class.


1 Answers

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.

like image 119
curiousguy Avatar answered Sep 24 '22 17:09

curiousguy