问题描述
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
}
为什么默认隐藏模板基类的公共成员类型?
解决方法
进一步研究的搜索词是“两阶段名称查找”。
简而言之,编译器尝试在定义模板的地方解析尽可能多的模板使用的名称。一些名称——所谓的“依赖名称”——此时无法解析,必须推迟到模板实际实例化并知道其参数。粗略地说,这些是从语法中出现的名称,取决于模板参数。
U
本身不是依赖名称,因此编译器会在编译 B
的定义时尝试解析它。它此时无法查看 A<T>
的内部 - 它不知道 T
将是什么,或者是否会针对该 A
专门化 T
可能会也可能不会声明名为 U
的成员。然后查找没有找到 U
的声明,因此出现错误。
U
中的 A<T>::U
是一个依赖名称,它的查找被推迟到 B<int>
被实例化。此时,编译器还会实例化 A<int>
并可以在其中查找 U
的声明。
这也是您需要在 typename
中写入 typename A<T>::U
的原因。编译器无法查找依赖名称 U
的声明,但它至少需要知道它是类型还是非类型,因为低级词法分析依赖于此(经典的例如,X*Y;
可以被解析为一个指针的声明,或者一个使用乘法的表达式,这取决于 X
是否是一个类型)。所以规则是,除非前面有 typename
关键字,否则依赖名称被假定为引用非类型。