【问题标题】:Template alias visibility in nested class嵌套类中的模板别名可见性
【发布时间】:2015-11-17 00:00:16
【问题描述】:

考虑以下几点:

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'
    };
};

好的,也许在派生嵌套类B 时,类A 中模板别名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>;
    };
};

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

(在 Clang 3.6 和 GCC 4.9.2 上测试)

【问题讨论】:

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


    【解决方案1】:

    简而言之:派生自Z 的特化引入了::Zinjected-class-name,它位于别名模板之前。但是,如果A 是一个模板,则找不到injected-class-name,因为B 的基类是依赖的。


    考虑 [temp.local]/1:

    与普通(非模板)类一样,类模板有一个 injected-class-name(第 9 条)。 可以使用 injected-class-name 作为 模板名称类型名称

    还有 [basic.lookup]/3:

    类的injected-class-name(第 9 条)也被认为是该类的成员,用于名称 […] 查找

    Z 被查找为非限定名称; [basic.lookup.unqual]/7:

    因此,injected-class-name Z 被发现是基类 Z&lt;B, int&gt; 的成员,并用作 模板名称,它使您的第二个程序格式错误。事实上,您的第一个 sn-p 也使用 injected-class-name - 以下 sn-p 不会编译:

    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&lt;B&gt; 是根据 [temp.dep.type]/(9.7) 的依赖类型,因此在查找 unqualified-id Z 时不会检查 基类 Z&lt;B&gt; > 根据 [temp.dep]/3:

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

    因此将找不到 injected-class-name


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

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

    【讨论】:

    • 哇。这个很清楚,谢谢。当A 不再是模板时,错误实际上出现了,我认为这会大大简化代码。但是,我现在不得不为两个Z 使用两个不同的名称,这只会使代码更难看。如果有更好的解决方法,请告诉我。
    • @iavr using C = Z; 怎么样? (不适用于 A 作为模板)
    • 现在这令人印象深刻 :-) 是的,它适用于这里的简化代码,但不适用于我的原始代码 (unknown type name 'Z')。我得检查一下区别在哪里。 A 不再是模板,我打算保留它。
    • 啊,那是因为我将外部的Z 重命名了(比如,改名为Z1)。现在它正在工作并且非常简化,因为我根本不需要using C ...。太好了,再次感谢!
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-11-30
    • 1970-01-01
    • 2013-06-22
    • 1970-01-01
    • 2018-09-22
    相关资源
    最近更新 更多