Why Doesn't a Derived Template Class Have Access to a Base Template Class' Identifiers

Why doesn't a derived template class have access to a base template class' identifiers?

That's two-phase lookup for you.

Base<T>::NO_ZEROFILL (all caps identifiers are boo, except for macros, BTW) is an identifier that depends on T.

Since, when the compiler first parses the template, there's no actual type substituted for T yet, the compiler doesn't "know" what Base<T> is. So it cannot know any identifiers you assume to be defined in it (there might be a specialization for some Ts that the compiler only sees later) and you cannot omit the base class qualification from identifiers defined in the base class.

That's why you have to write Base<T>::NO_ZEROFILL (or this->NO_ZEROFILL). That tells the compiler that NO_ZEROFILL is something in the base class, which depends on T, and that it can only verify it later, when the template is instantiated. It will therefore accept it without trying to verify the code.

That code can only be verified later, when the template is instantiated by supplying an actual parameter for T.

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 base function cannot be accessed by derived class when involving template classes

The name foo() does not depend on any of Derived's template parameters - it's a non-dependent name. The base class where foo() is found, on the other hand - Base<T> - does depend on one of Derived's template parameters (namely, T), so it's a dependent base class. C++ does not look in dependent base classes when looking up non-dependent names.

To resolve this, you need to qualify the call to bar() in Derived::foo() as either this->bar() or Base<T>::bar().

This C++ FAQ item explains it nicely: see http://www.parashift.com/c++-faq-lite/templates.html#faq-35.19

Is this- mandatory to access Base T identifiers from derived classes?

Clang is correct.

$17.6.2/3 Dependent names [temp.dep]

In the definition of a class or class template, the scope of a dependent base class is not examined during unqualified name lookup either at the point of definition of the class template or member or during an instantiation of the class template or member.

For return data;, data is unqualified, then unqualified name lookup will be employed. This means the name in the base class Base<T> (which is a dependent base class because it depends on the template parameter T) shouldn't be found; i.e. the non-dependent names are not looked up in dependent base classes.

this->data or Base<T>::data makes it qualified. This means the name will be looked up at the time of instantiation, and at that time the exact base specialization that must be explored will be known.

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.

C++ inheritance and access of protected base class member: is doing it Java-style a bad idea?

This has nothing to do with the member being protected; you'd get the exact same error with a public member.

The real reason is that templates are involed. More specifically, that your class template has a base class which depends on a template parameter. This means that when parsing the template, the compiler will not (be able to) look into that base class to find inherited members which are used unqualified. It makes sense: when the template is parsed, the template parameter values are not known yet and thus the compiler has no idea what members the base class will have (remember partial and total specialisation exist!).

In order to overcome this, you must somehow tell the compiler that the name foo depends on template parameters (that it's a dependent name). Once you do so, the compiler will not try to resolve it when parsing the template; it will postpone resolution until the template is instantiated. At that point, template arguments are known and thus the base class can be checked.

You have three ways to mark a member name as dependent:

  1. Refer to the name through this, as you're doing: this->foo

  2. Refer to the name through base-class qualification: BaseClass<T>::foo

  3. Bring the name into the scope of the derived class:

    template<class T>
    class DerivedClass : public BaseClass<T> {
    protected:
    using BaseClass<T>::foo; // from this point on, `foo` is a dependent name
    public:
    void incr_foo() {
    ++foo; // works fine now
    }
    };

Can not access member type from base-class when compiled using c++17

When the base class B class depends upon the template parameters, even though derived class D here type alias AbcData inherited from the B, using simply AbcData in D class, is not enough.

You need to be explicit, from where you have it

template<typename ...Args>
class D : public B<float, Args...>
{
public:
typename B<float, Args...>::AbcData m_abc; // --> like this
};

C++ template class and inheritance

The problem here has to do with how names are looked up in template classes that inherit from template base classes. The actual rules behind it are pretty arcane and I don't know them off the top of my head; I usually have to consult a reference to learn precisely why this doesn't work.

The way to fix this is to explicitly prefix the member you're accessing with this->:

void foo() { 
this->amount = this->amount * 2; // Or: this->amount *= 2;
}

This gives the compiler an unambiguous hint about where the name amount comes from and should resolve the compiler error.

If someone wants to give a more detailed description of why this error occurs I'd love to see a good explanation.

Inherited member function seems undeclared, what is going on?

The problem is that your derived class is a dependent name. You need to qualify the call to gather() in the base class with this->gather() instead, otherwise the compiler does not resolve gather() from the base class. To simplify,

template<class T> struct Base
{
void f();
};

template<class T> struct Derived: Base<T> // dependent name
{
void foo()
{
this->f(); // won't compile without this->
}
};

An alternative is to do

using Base::f;

in the derived Derived::foo() or qualify the call like

Base::f();

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



Related Topics



Leave a reply



Submit