包含命名空间的 class 模板的前向声明会导致编译错误

forward declaration of a class template including a namespace causes compile-error

我有一个 example1 和一个 example2,它们使用 class 模板的前向声明,前缀为相应 class 模板的名称空间。第一个示例使用 visual-studio 可以很好地编译,而第二个示例则不能。我针对其他编译器 (http://rextester.com) 检查了这两个示例。现在我有两个问题:

示例 1:

#include <vector>

namespace N1
{
    template <typename T> struct MySystem {};
    template <typename T> class Other {};

    struct MyClass
    {
        MyClass() { typename Dependencies::TYPE_A oTYPE_A; }

        struct Dependencies
        {
            template <typename>
            class N1::Other;

            struct TypeX_Dependencies;

            using TYPE_A = N1::MySystem<N1::Other<TypeX_Dependencies>>;

            struct TypeX_Dependencies
            {
                using TYPE_A = typename Dependencies::TYPE_A;
            };

            using TYPE_X = N1::Other<TypeX_Dependencies>;
        };
    };
}

int main(){ return 0; }

c++ (gcc 5.4.0)
source_file.cpp:15:23: error: invalid use of template-name ‘N1::Other’ without an argument list class N1::Other;

c++ (clang 3.8.0)
source_file.cpp:15:23: 错误:非好友 class 成员 'Other' 不能有限定名称 class N1::Other;<br> source_file.cpp:15:19: 错误:class 的前向声明不能有嵌套名称说明符 class N1::Other;

c++(vc++ 19.00.23506 for x64 / 也与 community 2017 15.4.4 相比)
compiles fine

示例 2:

#include <vector>

namespace N1
{
    template <typename T> struct MySystem {};
    template <typename T> class Other {};

    template <typename T>
    struct MyClass
    {
        MyClass() { typename Dependencies::TYPE_A oTYPE_A; }

        struct Dependencies
        {
            template <typename>
            class N1::Other;

            struct TypeX_Dependencies;

            using TYPE_A = N1::MySystem<N1::Other<TypeX_Dependencies>>;

            struct TypeX_Dependencies
            {
                using TYPE_A = typename Dependencies::TYPE_A;
            };

            using TYPE_X = N1::Other<TypeX_Dependencies>;
        };
    };
}

int main(){ return 0; }

c++ (gcc 5.4.0)
source_file.cpp:16:23: error: invalid use of template-name ‘N1::Other’ without an argument list class N1::Other;

c++ (clang 3.8.0)
source_file.cpp:16:23: 错误:非好友 class 成员 'Other' 不能有限定名称 class N1::Other;<br> source_file.cpp:16:19: 错误:class 的前向声明不能有嵌套名称说明符 class N1::Other;

c++(vc++ 19.00.23506 for x64 / 也与 community 2017 15.4.4 相比)
source_file.cpp(16): 错误 C3855: 'N1::Other': 模板参数 'T' 与声明 <br> 不兼容 source_file.cpp(24):注意:参见参考 class 模板实例化 'N1::MyClass<T>::Dependencies' 正在编译<br> source_file.cpp(29):注意:参见参考 class 模板实例化 'N1::MyClass<T>' 正在编译 <br> source_file.cpp(20):错误 C3203:'Other':未专门化的 class 模板不能用作模板参数 'T' 的模板参数,需要一个真实类型

是的,这是一个错误。

根据[dcl.type.elab]/1,

... If an elaborated-type-specifier is the sole constituent of a declaration, the declaration is ill-formed unless it is an explicit specialization, an explicit instantiation or it has one of the following forms:

  • class-key attribute-specifier-seqopt identifier ;
  • friend class-key ::opt identifier ;
  • friend class-key ::opt simple-template-id ;
  • friend class-key nested-name-specifier identifier ;
  • friend class-key nested-name-specifier templateopt simple-template-id ;

您的声明 class N1::Other 既不是显式特化,也不是显式实例化,因此它必须具有强调形式(忽略那些 friend 声明)。请注意,在强调形式的 identifier 之前不允许使用 nested-name-specifier,因此声明格式错误。下面这个没有template的例子中Clang的编译错误就说明了这个问题。

namespace N {
    struct S;
}
struct N::S; // error: forward declaration of struct cannot have a nested name specifier

LIVE EXAMPLE(顺便说一句,GCC 接受此代码,只是给出警告,说明没有新内容要声明。我猜这是 GCC 的一个错误。)

这里的template-headtemplate <typename>没有帮助,因为class N1::Other本身根据[=的语法定义应该形成一个声明 40=]template-head,因此上面的段落适用。

粗略地说,在与声明同名 class 的范围不同的范围内对 class 的声明应该引入一个新的 class,在这种情况下 nested-name-specifier 不应使用,或定义先前声明的 class,在这种情况下语法形成 class-specifier 而不是 elaborated-type-specifier,因此上面的段落不适用。综上所述,这个规则是合理的。