继承嵌套模板类的专业化

Jun*_*eon 6 c++ inheritance inner-classes template-specialization language-lawyer

以下源代码来自: 了解继承的嵌套类模板的部分特化

#include <type_traits>
struct Base
{
    template<class U, class _ = void> struct Inner: std::true_type {};
    template<class _> struct Inner<char, _>: std::false_type {};
};
struct Derived : Base
{
};

template<class _> struct Derived::Inner<int, _>: std::false_type {};
Run Code Online (Sandbox Code Playgroud)

我有一个关于专门继承类的问题,所以我用Google搜索,并找出上面的问题.上面问题中的源代码没有编译gcc/clang中的任何问题,但msvc拒绝编译它,发出C2427(参见 https://msdn.microsoft.com/en-us/library/10het5hx.aspx).

上面的情况(专门化非模板类的嵌套模板类)与https://msdn.microsoft.com/en-us/library/10het5hx.aspx中描述的情况完全不同(定义嵌套的非我认为,模板类的模板类).

msvc与gcc/clang中哪一个错了?或者只是标准是如此不明确指定这种行为?

我希望msvc错了......

bog*_*dan 2

Clang 和 GCC 是错误的,MSVC 和 EDG 拒绝部分专业化定义是正确的。

部分特化本身就是一个模板,并且类模板定义是根据类定义(在语法术语中,类说明符)在语法上构造的。在这样的定义中,Derived::Inner<int, _>是一个类头名称,并且Derived::是一个嵌套名称说明符

标准中的 [9p11] 说:

如果类头名称包含嵌套名称说明符,则该类 说明符应引用先前直接在嵌套名称说明符 引用的类或命名空间中声明的类,或者在该类的元素中声明的类。该命名空间的内联命名空间集(7.3.1)(即不仅仅是由 using 声明继承或引入),并且类说明符应出现在包含先前声明的命名空间中。[...]

所以,你必须使用Base::Inner<int, _>.


正如评论中所指出的,上面的引用也适用于类模板显式专业化定义(它们的语法生成也最终使用class-head-name)。


以下内容并不直接适用于您的示例,但我发现值得一提。

请注意,上面的引用指的是类模板(或显式专业化)定义,而不是声明,例如

template<class _> struct Derived::Inner<int, _>;
Run Code Online (Sandbox Code Playgroud)

从语法上讲,struct Derived::Inner<int, _>其中有一个详细类型说明符,上面的段落不适用于该说明符。因此,标准措辞在技术上允许此类声明。

这似乎不是一个疏忽:上面的措辞是由DR284决议引入的,其中包括评论:

人们的观点是,这应该在类定义上需要,但在一般的详细类型说明符上不需要(它们是引用,而不是声明)。[...]

拟议的决议包括详细的类型说明符,但这些已从最终措辞中删除。

然而,MSVC 和 EDG 都不接受这样的声明(坦白说,如果他们这样做的话我会觉得很困惑)。DR 中的注释似乎表明其意图是仅允许不是声明的详细类型说明符,但看起来这并未反映在措辞中(我认为是一个标准错误)。