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

问题描述 投票:2回答:1

我有一个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

c++ templates compiler-errors namespaces forward-declaration
1个回答
1
投票

是的,这是一个错误。

根据[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而不是一个详细的类型说明符,因此上面的段落不适用。总之,这条规则是合理的。

© www.soinside.com 2019 - 2024. All rights reserved.