12

考虑以下:

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,类中模板别名的定义是可见的,但在其主体内不可见,这会触发错误,因为全局定义有两个参数。ABZ

但是为什么在第一种情况下行为不同,什么时候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 上测试)

4

1 回答 1

9

简而言之:从 的 特化派生Z引入了 的注入类名::Z它位于别名模板之前。但是,如果A是一个模板,则不再找到注入的类名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) 的依赖类型,因此根据 [temp.dep]/3,在查找unqualified-id期间不检查基类:Z<B> Z

在类 [..] 的定义中,在非限定名称查找期间,无论是在类模板或成员的定义点还是在类模板的实例化期间,都不会检查依赖基类 (14.6.2.1) 的范围或会员。

因此将找不到注入的类名。


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

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

于 2015-11-17T01:23:40.510 回答