Reputation: 42297
template<typename T>
struct A
{
using U = int;
};
struct B : A<int>
{
void f(U) // ok
{}
};
template<typename T>
struct C : A<int>
{
void f(U) // ok
{}
};
template<typename T>
struct D : A<T>
{
void f(U) // fatal error: unknown type name 'U'
{}
};
int main()
{
B b; // ok
C<int> c; // ok
D<int> d; // error
}
Why can't the class inherit the member types of its parents?
Upvotes: 2
Views: 48
Reputation: 172924
Because as a non-dependent name, U
won't be looked up in dependent base class A<T>
, which depends on template parameter T
. On the other hand, for both B
and C
their base class are non-dependent base class.
You can change to dependent name, e.g. A<T>::U
, which depends on template parameter T
too. Dependent names may be looked up only at the time of instantiation, at that time the exact specialization (including base class) will be known.
template<typename T>
struct D : A<T>
{
void f(typename A<T>::U)
{}
};
Upvotes: 3
Reputation: 119164
The member U
is inherited like any other member would be, irrespective of which classes are templated, but it is not found by unqualified name lookup according to C++17 [temp.dep]/3:
In the definition of a class or class template, the scope of a dependent base class (17.6.2.1) 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.
Here, A<T>
is a dependent base class since it depends on the template parameter T
of the class template D
.
To force the compiler to look up U
in the base class, you have to use qualified name lookup. You can do it like this:
void f(typename A<T>::U);
Another way of expressing it, if the template arguments to the base class are complicated, is:
void f(typename D::A::U);
If you are going to be writing this out multiple times then you could also redeclare the type in D
for convenience:
using U = typename A<T>::U;
void f(U);
Note: in the above contexts, typename
will become optional in C++20.
Upvotes: 4