Inheritance and Templates in C++ - Why Are Inherited Members Invisible

Visibility of members of base template class not directly inherited

You are using A<X> where a base class is expected.

[namespace.udecl]

3 In a using-declaration used as a member-declaration, each
using-declarator's nested-name-specifier shall name a base class of
the class being defined.

Since this appears where a class type is expected, it is known and assumed to be a type. And it is a type that is dependent on the template arguments, so it's not looked up immediately.

[temp.res]

9 When looking for the declaration of a name used in a template
definition, the usual lookup rules ([basic.lookup.unqual],
[basic.lookup.argdep]) are used for non-dependent names. The lookup of
names dependent on the template parameters is postponed until the
actual template argument is known ([temp.dep]).

So it's allowed on account of the compiler not being able to know any better. It will check the using declaration when the class is instantiated. Indeed, one can put any dependent type there:

template<bool> struct D{};

template <bool X>
struct C : public B<X> {
using D<X>::x;
C() { x = 1; }
};

This will not be checked until the value of X is known. Because B<X> can bring with it all sorts of surprises if it's specialized. One could for instance do this:

template<>
struct D<true> { char x; };

template<>
struct B<true> : D<true> {};

Making the above declaration be correct.

templates: parent class member variables not visible in inherited class

This is because the template parent of a template class is not instantiated during the compilation pass that first examines the template. These names appear to be non-dependent on the particular template instantiation, and therefore the definitions need to be available. (If you never look at the definition of arrayListType, then reading the code of unorderedArrayListType it would appear the list and length need to be some sort of globals.)

You'll need to tell the compiler explicitly that the names are in fact dependent on the instantiation of the parent.

One way, using this-> before all the inherited names: this->list, this->length.

Another way, using declarations: using arrayListType<elemType>::length; etc (for example in the private section of the derived class).


A FAQ entry on this: https://isocpp.org/wiki/faq/templates#nondependent-name-lookup-members

template base class typedef members invisible

You probably want to do:

using MemberType = typename TBase<T>::MemberType; // new type alias syntax

or

typedef typename TBase<T>::MemberType MemberType; // old type alias syntax

The syntax using Base::member; can only be used to bring the declarations of non-type members into scope.


Also note that none of these are actually required, you can qualify each use (for types with the base, for non-types with either this-> or the base) and that will make the symbol dependent.



Related Topics



Leave a reply



Submit