2

考虑以下:

template<class> struct T
{
    struct T1
    {
        struct T2
        {

        };
    };
    /*typename*/ T1::T2 m;
};

如果没有typename,编译会失败,因为 T2 被认为是从属名称,因此不是类型。在查看了 C++17 草案标准 (N4659) 之后,我相信我已经确定了原因:

§ 17.6.2.1 ¶ 5

一个名称是当前实例化的成员,如果它是
- 一个非限定名称,当查找时,它指的是作为当前实例化的类或其非依赖基类的至少一个成员。
...
如果名称是当前实例化的成员,并且在查找时引用了作为当前实例化的类的至少一个成员,则该名称是当前实例化的依赖成员。

T1 是当前实例化的从属成员。T2 不是当前实例化的成员(它是 T1 的成员),

§ 17.6.2.1 ¶ 9.3

一个类型是依赖的,如果它是
......
- 一个嵌套类或枚举,它是当前实例化的依赖成员,
......

T1 是一个嵌套类,因此是一个依赖类型。

§ 17.6 ¶ 3

qualified-id旨在引用不是当前实例化(17.6.2.1)成员的类型并且其nested-name-specifier引用依赖类型时,它应以关键字typename为前缀,形成一个类型名说明符。...

因此,typename需要。

我的理解正确吗?如果是这样,这背后的理由是什么?除了嵌套在 T1 中的 T2 之外,查找如何T1::T2找到任何东西?

4

1 回答 1

4

是的,你是对的。

在您的情况下,这无关紧要,因为如果T1不实例化m(因为它是类成员),您就不可能专门化。但是你可以这样做,放入m一个函数:

template<class> struct T
{
    struct T1
    {
        struct T2
        {

        };
    };

    void foo() {
        typename T1::T2 m;
    }
};

template<>
struct T<void>::T1 {
    int T2;
};

如果T1不依赖,您的代码可能会改变含义,因为T2将引用一个值而不是类型。

于 2018-06-29T16:45:50.897 回答