包含命名空间的类模板的转发声明会导致编译错误

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了包含命名空间的类模板的转发声明会导致编译错误相关的知识,希望对你有一定的参考价值。

我有一个example1和一个example2,它使用前缀为相应类模板命名空间的类模板的前向声明。第一个例子用visual-studio编译好,而第二个例子没有。我检查了两个例子与其他编译器(http://rextester.com)。现在我有两个问题:

  • 在这里使用前向声明中的命名空间似乎是非法的。为什么呢?
  • Visual Studio(2015和2017)似乎允许在第一个示例中使用附加命名空间进行前向声明,但在第二个示例中不允许。这是一个错误吗?

例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: error: non-friend class member 'Other' cannot have a qualified name class N1::Other;
source_file.cpp:15:19: error: forward declaration of class cannot have a nested name specifier class N1::Other;

c ++(vc ++ 19.00.23506 for x64 / also vs 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: error: non-friend class member 'Other' cannot have a qualified name class N1::Other;
source_file.cpp:16:19: error: forward declaration of class cannot have a nested name specifier class N1::Other;

c ++(vc ++ 19.00.23506 for x64 / also vs community 2017 15.4.4) source_file.cpp(16): error C3855: 'N1::Other': template parameter 'T' is incompatible with the declaration
source_file.cpp(24): note: see reference to class template instantiation 'N1::MyClass<T>::Dependencies' being compiled
source_file.cpp(29): note: see reference to class template instantiation 'N1::MyClass<T>' being compiled
source_file.cpp(20): error C3203: 'Other': unspecialized class template can't be used as a template argument for template parameter 'T', expected a real type

答案

是的,这是一个错误。

根据[dcl.type.elab]/1

......如果elaborated-type-specifier是声明的唯一成分,则声明格式不正确,除非它是明确的专门化,明确的实例化或者它具有以下形式之一:

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

你的声明class N1::Other既不是一个明确的专业化,也不是一个明确的实例化,所以它必须有强调的形式(忽略那些friend声明)。注意在强调形式的标识符之前不允许使用nested-name-specifier,因此声明格式不正确。在没有模板的以下示例中,Clang的编译器错误显示此问题。

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

LIVE EXAMPLE(顺便说一句,GCC接受这个代码并且只是发出警告,没有任何新的声明。我想这是GCC的一个错误。)

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

粗略地说,与声明具有相同名称的类的范围不同范围的类的声明应该引入新类,在这种情况下不应使用nested-name-specifier,或者定义先前声明的类,在哪种情况下,语法形成一个class-specifier而不是一个详细的类型说明符,因此上面的段落不适用。总之,这条规则是合理的。

以上是关于包含命名空间的类模板的转发声明会导致编译错误的主要内容,如果未能解决你的问题,请参考以下文章

静态库命名空间内简单函数的编译错误

我如何声明一个名称为静态的类

命名空间中的类并使用模板类型作为返回类型时的全局范围友元运算符声明

在命名空间中转发声明类

如何在类模板中使用文件范围的命名空间声明?

C#中的命名空间和程序集