2015-11-17 94 views
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' 
    }; 
}; 

好吧,也许这是有道理的,派生的嵌套类BA类模板别名Z的定义是可见的,而不是它的体内,引发由于错误全局定义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>; 
    }; 
}; 

现在错误消失。 为什么?

(测试锵3.6和GCC 4.9.2)

回答

9

简而言之:从Z专业化推导介绍注入类名的::Z,其别名模板之前找到。如果A是一个模板,但是,注入的类名不再被发现,因为基类的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),因此Z<B>是依据[temp.dep.type] /(9.7)的依赖类型,因此在查找期间未检查基类Z<B>不合格IDZ根据[temp.dep]/3:

在一个类的定义[..],一个 依赖性基类(14.6.2.1)的范围并不在任一类模板或 构件的定义点不合格 名查找期间检查或者在类模板或成员的实例化期间。

因此注入类名称将不会被找到。


B是 “嵌套类[..]这是一个依赖构件当前实例的”(重点煤矿),由于

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

+0

哇。这很清楚,谢谢。当'A'停止作为模板时,实际上出现了这个错误,我认为这会简化代码。然而,我现在被迫为两个'Z'使用两个不同的名字,这只会使代码更丑。如果有更好的解决方法,请告诉我。 – iavr

+0

@iavr如何使用C = Z;'? (不适用于'A'作为模板) – Columbo

+0

现在令人印象深刻:-)是的,它适用于此简化代码,但不适用于我的原始代码('未知类型名称'Z'')。我将不得不检查其中的差异。 'A'不再是一个模板,我打算保持这种模式。 – iavr