嵌套类中的模板别名可见性

iav*_*avr 12 c++ nested-class language-lawyer c++11 template-aliases

考虑以下:

template<typename X>
struct Z {};

struct A
{
    using Z = ::Z<int>;

    struct B : Z
    {
        using C = Z;
    };
};
Run Code Online (Sandbox Code Playgroud)

编译好了.尼斯.但现在添加另一个参数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'
    };
};
Run Code Online (Sandbox Code Playgroud)

好吧,也许有意义的是,在派生嵌套类时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>;
    };
};
Run Code Online (Sandbox Code Playgroud)

现在错误消失了.为什么?

(经过Clang 3.6和GCC 4.9.2测试)

Col*_*mbo 9

总之:从一个特例推导Z介绍了注射类名::Z,这是别名模板之前找到.A但是,如果是模板,则不再找到inject -class-name,因为基类B是依赖的.


考虑[temp.local]/1:

与普通(非模板)类一样,类模板具有 注入类名(第9节).所述注入的类名可以被用作模板名称类型名称.

并且[basic.lookup]/3:

为了名称查找的目的,类注入类名(第9条)也被认为是该类的成员.

Z被视为不合格的名字; [basic.lookup.unqual]/7:

在此输入图像描述

因此,inject-class-name Z被发现为基类的成员 Z<B, int>,并用作模板名称,这会使您的第二个程序格式错误.实际上,您的第一个代码段也使用了inject-class-name - 以下代码段将无法编译:

struct A
{
    using Z = ::Z<float>;
    struct B : ::Z<int>
    {
        static_assert( std::is_same<Z, ::Z<float>>{}, "" );
    };
};
Run Code Online (Sandbox Code Playgroud)

最后,如果A由模板,请注意,B是一个依赖型按照[temp.dep.type] /(9.3)1,因而Z<B>是一个依赖型按照[temp.dep.type] /(9.7),从而将根据[temp.dep]/3 查找unqualified-id期间不检查基类Z<B> Z:

在类[..]的定义中,在类模板或成员的定义点或类模板的实例化期间,在非限定名称查找期间不检查从属基类(14.6.2.1)的范围.或成员.

因此,将找不到注入类名.


1 B是"嵌套类[..],它是当前实例化的依赖成员 "(强调我的),因为

如果名称是当前实例化的成员,则该名称是当前实例化的成员,当查找时,该名称是指当前实例化的类的至少一个成员.