嵌套类

时间:2015-11-17 00:00:16

标签: c++ c++11 language-lawyer nested-class template-aliases

请考虑以下事项:

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上测试)

1 个答案:

答案 0 :(得分:9)

简而言之:从Z的特化引入,引入了::Z注入类名,它位于别名模板之前。但是,如果A是模板,则不再找到 inject-name-name ,因为B的基类是依赖的。

考虑[temp.local] / 1:

  

与普通(非模板)类一样,类模板也有   注入类名(第9条)。 可以使用注入类名   作为模板名称类型名称

和[basic.lookup] / 3:

  

类的注入类名(第9条)也被视为该类的成员,用于名称[...]查找

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

enter image description here

因此,注入类名 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:

,em> Z
  

在类[..]的定义中,a。的范围   在不合格的情况下,不检查依赖基类(14.6.2.1)   名称查找在类模板的定义点或   成员或在类模板或成员的实例化期间。

因此找不到注入类名

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

  

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