In a Templated Derived Class, Why Do I Need to Qualify Base Class Member Names With "This-≫" Inside a Member Function

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

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.

Derived template-class access to base-class member-data

You can use this-> to make clear that you are referring to a member of the class:

void Bar<T>::BarFunc () {
std::cout << this->_foo_arg << std::endl;
}

Alternatively you can also use "using" in the method:

void Bar<T>::BarFunc () {
using Bar<T>::_foo_arg; // Might not work in g++, IIRC
std::cout << _foo_arg << std::endl;
}

This makes it clear to the compiler that the member name depends on the template parameters so that it searches for the definition of that name in the right places. For more information also see this entry in the C++ Faq Lite.

Why do I have to access template base class members through the this pointer?

Short answer: in order to make x a dependent name, so that lookup is deferred until the template parameter is known.

Long answer: when a compiler sees a template, it is supposed to perform certain checks immediately, without seeing the template parameter. Others are deferred until the parameter is known. It's called two-phase compilation, and MSVC doesn't do it but it's required by the standard and implemented by the other major compilers. If you like, the compiler must compile the template as soon as it sees it (to some kind of internal parse tree representation), and defer compiling the instantiation until later.

The checks that are performed on the template itself, rather than on particular instantiations of it, require that the compiler be able to resolve the grammar of the code in the template.

In C++ (and C), in order to resolve the grammar of code, you sometimes need to know whether something is a type or not. For example:

#if WANT_POINTER
typedef int A;
#else
int A;
#endif
static const int x = 2;
template <typename T> void foo() { A *x = 0; }

if A is a type, that declares a pointer (with no effect other than to shadow the global x). If A is an object, that's multiplication (and barring some operator overloading it's illegal, assigning to an rvalue). If it is wrong, this error must be diagnosed in phase 1, it's defined by the standard to be an error in the template, not in some particular instantiation of it. Even if the template is never instantiated, if A is an int then the above code is ill-formed and must be diagnosed, just as it would be if foo wasn't a template at all, but a plain function.

Now, the standard says that names which aren't dependent on template parameters must be resolvable in phase 1. A here is not a dependent name, it refers to the same thing regardless of type T. So it needs to be defined before the template is defined in order to be found and checked in phase 1.

T::A would be a name that depends on T. We can't possibly know in phase 1 whether that's a type or not. The type which will eventually be used as T in an instantiation quite likely isn't even defined yet, and even if it was we don't know which type(s) will be used as our template parameter. But we have to resolve the grammar in order to do our precious phase 1 checks for ill-formed templates. So the standard has a rule for dependent names - the compiler must assume that they're non-types, unless qualified with typename to specify that they are types, or used in certain unambiguous contexts. For example in template <typename T> struct Foo : T::A {};, T::A is used as a base class and hence is unambiguously a type. If Foo is instantiated with some type that has a data member A instead of a nested type A, that's an error in the code doing the instantiation (phase 2), not an error in the template (phase 1).

But what about a class template with a dependent base class?

template <typename T>
struct Foo : Bar<T> {
Foo() { A *x = 0; }
};

Is A a dependent name or not? With base classes, any name could appear in the base class. So we could say that A is a dependent name, and treat it as a non-type. This would have the undesirable effect that every name in Foo is dependent, and hence every type used in Foo (except built-in types) has to be qualified. Inside of Foo, you'd have to write:

typename std::string s = "hello, world";

because std::string would be a dependent name, and hence assumed to be a non-type unless specified otherwise. Ouch!

A second problem with allowing your preferred code (return x;) is that even if Bar is defined before Foo, and x isn't a member in that definition, someone could later define a specialization of Bar for some type Baz, such that Bar<Baz> does have a data member x, and then instantiate Foo<Baz>. So in that instantiation, your template would return the data member instead of returning the global x. Or conversely if the base template definition of Bar had x, they could define a specialization without it, and your template would look for a global x to return in Foo<Baz>. I think this was judged to be just as surprising and distressing as the problem you have, but it's silently surprising, as opposed to throwing a surprising error.

To avoid these problems, the standard in effect says that dependent base classes of class templates just aren't considered for search unless explicitly requested. This stops everything from being dependent just because it could be found in a dependent base. It also has the undesirable effect that you're seeing - you have to qualify stuff from the base class or it's not found. There are three common ways to make A dependent:

  • using Bar<T>::A; in the class - A now refers to something in Bar<T>, hence dependent.
  • Bar<T>::A *x = 0; at point of use - Again, A is definitely in Bar<T>. This is multiplication since typename wasn't used, so possibly a bad example, but we'll have to wait until instantiation to find out whether operator*(Bar<T>::A, x) returns an rvalue. Who knows, maybe it does...
  • this->A; at point of use - A is a member, so if it's not in Foo, it must be in the base class, again the standard says this makes it dependent.

Two-phase compilation is fiddly and difficult, and introduces some surprising requirements for extra verbiage in your code. But rather like democracy it's probably the worst possible way of doing things, apart from all the others.

You could reasonably argue that in your example, return x; doesn't make sense if x is a nested type in the base class, so the language should (a) say that it's a dependent name and (2) treat it as a non-type, and your code would work without this->. To an extent you're the victim of collateral damage from the solution to a problem that doesn't apply in your case, but there's still the issue of your base class potentially introducing names under you that shadow globals, or not having names you thought they had, and a global being found instead.

You could also possibly argue that the default should be the opposite for dependent names (assume type unless somehow specified to be an object), or that the default should be more context sensitive (in std::string s = "";, std::string could be read as a type since nothing else makes grammatical sense, even though std::string *s = 0; is ambiguous). Again, I don't know quite how the rules were agreed. My guess is that the number of pages of text that would be required, mitigated against creating a lot of specific rules for which contexts take a type and which a non-type.

Why inherited member function from base template class not declared?

ADL is argument dependent lookup. A function name is looked for in the associated namespaces of its arguments. int has no associated namespaces, so no ADL occurs. Even if it did occur, it would only find free functions, so your method would not be found.

Accessing base member functions in class derived from template class

When a class template derives from a base class template, the base members are not visible in the derived class template definition. (This makes sense; until you specialize, there is no class, and so there are no members. Explicit specializations can always change the meaning of any given template class.)

In other words, the base template member names are dependent names and not looked up in the first phase of template definition lookup.

There are three ways to get around this. Let's make it concrete with a quick example:

template <typename T> struct Foo
{
int data;
using type = const T &;
void gobble() const;
template <int N> void befuddle();
};

template <typename T> struct X : Foo<T> { /* ... */ };

Now in the context of the derived class template definition, you can...

  1. Qualify the name:

    Foo<T>::data = 10;
    typename Foo<T>::type x;
    Foo<T>::gobble();
    Foo<T>::template befuddle<10>();
  2. Use this:

    this->data = 10;
    this->gobble();
    this->template befuddle<10>();

    (This doesn't work for type names names.)

  3. Use a using declaration:

    using Foo<T>::data;
    using Foo<T>::gobble;
    using type = typename Foo<T>::type;

    data = 10;
    gobble();

    (This doesn't work for template names.)


Update: After your edit, the question is entirely different. Templates don't play a role at all here, since the problem doesn't contain templates, only classes. What's happening is the simple fact that member functions in a derived class hide member functions of the same name in base classes, so the presence of SpecificDerived2::memberFunc hides the base member function. The simple solution is to unhide base members of the same name with a using declaration:

class SpecificDerived2 : public TemplateBase2<float>
{
public:
using TemplateBase2<float>::memberFunc;
// ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

float memberFunc()
{
return 3.14;
}
};

How to declare a member in a base template class where the type is dependent of the derived class?

Is it possible to make a member type directly dependent on the derived class? Taking appart the result type of a member function declared with auto (deduced return type), it is not possible.

So the use of a type-trait as you do in your solution is the best and only solution.

The reason is that a base class must be a complete type when the derived class is defined: the compiler must first instantiate and parse the base class definition before it parses the derived class definition, C++ standard N4140 [derived.class]/2 (bold is mine):

The type denoted by a base-type-specifier shall be a class type that is not an incompletely defined class;[...]

GCC issue: using a member of a base class that depends on a template argument

This changed in gcc-3.4. The C++ parser got much more strict in that release -- per the spec but still kinda annoying for people with legacy or multi-platform code bases.

GCC error when using parent class method as derived class method

There is namespace.udecl, item 12 (emphasis mine):

For the purpose of forming a set of candidates during overload
resolution, the functions named by a using-declaration in a derived
class are treated as though they were direct members of the derived
class. [...] This has no effect on the type of the function,
and in all other respects the function remains part of the base class
.

Thus, a is not a member of B, and the type of &B::a is int (A::*)().

(&B::a means the same thing regardless of whether you include using A::a; or not)

There is no point to using named functions from a base class except to work around the "hiding problem" when you want to overload or override them.



Related Topics



Leave a reply



Submit