请考虑以下事项:
template<typename X>
struct Z {};
struct A
{
using Z = ::Z<int>;
struct B : Z
{
using C = Z;
};
};
编译好。尼斯。但现在在Z
中添加另一个参数:
template<typename X, typename Y>
struct Z {};
struct A
{
template<typename X>
using Z = ::Z<X, int>;
struct B : Z<B>
{
using C = Z<B>; // error: too few template arguments for class template 'Z'
};
};
好吧,也许有意义的是,类Z
中的模板别名A
的定义在派生嵌套类B
时是可见的,但在其体内不可见,从而触发错误Z
的全局定义有两个参数。
但为什么是第一种情况下的行为不同,Z
只是A
中的类型别名?
最后,将A
设为模板:
template<typename X, typename Y>
struct Z {};
template<typename T>
struct A
{
template<typename X>
using Z = ::Z<X, int>;
struct B : Z<B>
{
using C = Z<B>;
};
};
现在错误消失了。的为什么吗
(在Clang 3.6和GCC 4.9.2上测试)
答案 0 :(得分:9)
简而言之:从Z
的特化引入,引入了::Z
的注入类名,它位于别名模板之前。但是,如果A
是模板,则不再找到 inject-name-name ,因为B
的基类是依赖的。
考虑[temp.local] / 1:
与普通(非模板)类一样,类模板也有 注入类名(第9条)。 可以使用注入类名 作为模板名称或类型名称 。
和[basic.lookup] / 3:
类的注入类名(第9条)也被视为该类的成员,用于名称[...]查找。
Z
被视为不合格的名字; [basic.lookup.unqual] / 7:
因此,注入类名 Z
被发现为基类Z<B, int>
的成员,并用作模板名称,这会使你的第二个程序变得格格不入。实际上,您的第一个代码段也使用注入类名 - 以下代码段将无法编译:
struct A
{
using Z = ::Z<float>;
struct B : ::Z<int>
{
static_assert( std::is_same<Z, ::Z<float>>{}, "" );
};
};
最后,如果A
成为模板,请注意B
是依赖于[temp.dep.type] /(9.3) 1 的依赖类型,因此Z<B>
是依赖于[temp.dep.type] /(9.7)的依赖类型,因此在查找 unqualified-id >时不检查基类Z<B>
根据[temp.dep] / 3:
Z
在类[..]的定义中,a。的范围 在不合格的情况下,不检查依赖基类(14.6.2.1) 名称查找在类模板的定义点或 成员或在类模板或成员的实例化期间。
因此找不到注入类名。
1 B
是一个“嵌套类[..],它是当前实例化的依赖成员”(强调我的),因为
如果是,则名称是当前实例化的依赖成员 当前实例化的成员,当查找时,指的是 至少有一个类是当前实例化的成员。