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 ofB
. It cannot look insideA<T>
at this time - it doesn't know whatT
is going to be, or whether there's going to be a specialization ofA
for thatT
that may or may not declare a member namedU
. The lookup then doesn't find a declaration forU
, hence the error.U
inA<T>::U
is a dependent name, its lookup is deferred untilB<int>
is instantiated. At that point, the compiler also instantiatesA<int>
and can look inside it for the declaration ofU
.This is also why you need to write
typename
intypename A<T>::U
. The compiler cannot look up the declaration of the dependent nameU
, 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 thatX*Y;
could be parsed as a declaration of a pointer, or an expression using multiplication, depending on whetherX
is a type). So the rule is, the dependent name is assumed to refer to a non-type unless preceded bytypename
keyword.