template<typename T>
struct A
{
using U = T;
};
template<typename T>
struct B : A<T>
{
B(typename A<T>::U) {} // okay
B(U) {} // error: unknown type name 'U'
};
int main()
{
return typename B<int>::U{}; // okay
}
Why is a template base class' public member types hidden by default?
The search term for further research is "two-phase name lookup".
In short, the compiler tries to resolve as many names used by the template as possible at the point where the template is defined. Some names - so-called "dependent names" - cannot be resolved at this point, and have to be deferred until the template is actually instantiated and its parameters become known. Roughly, these are names that appear from the syntax to depend on the template parameters.
U
by itself is not a dependent name, and so the compiler tries to resolve it while compiling the definition of B
. It cannot look inside A<T>
at this time - it doesn't know what T
is going to be, or whether there's going to be a specialization of A
for that T
that may or may not declare a member named U
. The lookup then doesn't find a declaration for U
, hence the error.
U
in A<T>::U
is a dependent name, its lookup is deferred until B<int>
is instantiated. At that point, the compiler also instantiates A<int>
and can look inside it for the declaration of U
.
This is also why you need to write typename
in typename A<T>::U
. The compiler cannot look up the declaration of the dependent name U
, but it needs to know at least whether it's meant to be a type or a non-type, as the low-level lexical analysis depends on that (the classic example is that X*Y;
could be parsed as a declaration of a pointer, or an expression using multiplication, depending on whether X
is a type). So the rule is, the dependent name is assumed to refer to a non-type unless preceded by typename
keyword.