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

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

本文关键字:编译 错误 声明 命名空间 包含      更新时间:2023-10-16

我有一个示例 1 和一个 example2,它使用以相应类模板的命名空间为前缀的类模板的前向声明。第一个示例使用可视化工作室编译良好,而第二个示例则不能。我根据其他编译器(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/也与社区 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/也与社区 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,

。如果详细类型说明符是声明的唯一组成部分,则该声明的格式不正确,除非它是显式专用化、显式实例化或具有以下形式之一:

  • 类键属性说明符-seqoptidentifier ;
  • friend类键::选择标识符;
  • friend类键::选择简单模板 ID;
  • friend类键嵌套名称说明符标识符;
  • friend类键嵌套名称说明符template选择简单模板 ID;

您的声明class N1::Other既不是显式专用化,也不是显式实例化,因此它必须具有强调形式(忽略声明friend)。请注意,在强调形式的标识符之前不允许嵌套名称说明符,因此声明格式不正确。下面没有模板的示例中 Clang 的编译器错误显示了此问题。

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

活生生的例子(顺便说一句,GCC 接受此代码,只是给出一个警告,说明没有要声明的新内容。我想这是海湾合作委员会的错误。

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

粗略地说,与声明具有相同名称的类的范围不同范围内的类声明应该引入一个新类,在这种情况下不应使用嵌套名称说明符,或者定义先前声明的类,在这种情况下,语法形成类说明符而不是详细类型说明符,因此上面的段落不适用。作为结论,这个规则是合理的。