包含命名空间的 class 模板的前向声明会导致编译错误
forward declaration of a class template including a namespace causes compile-error
我有一个 example1 和一个 example2,它们使用 class 模板的前向声明,前缀为相应 class 模板的名称空间。第一个示例使用 visual-studio 可以很好地编译,而第二个示例则不能。我针对其他编译器 (http://rextester.com) 检查了这两个示例。现在我有两个问题:
在前向声明中使用命名空间似乎是非法的。为什么呢?
Visual studio(2015 和 2017)似乎允许在 first 示例中使用附加名称空间进行前向声明,但在第二个示例中不是。这是一个错误吗?
示例 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' 的模板参数,需要一个真实类型
是的,这是一个错误。
... 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 template
opt 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,因此上面的段落不适用。综上所述,这个规则是合理的。
我有一个 example1 和一个 example2,它们使用 class 模板的前向声明,前缀为相应 class 模板的名称空间。第一个示例使用 visual-studio 可以很好地编译,而第二个示例则不能。我针对其他编译器 (http://rextester.com) 检查了这两个示例。现在我有两个问题:
在前向声明中使用命名空间似乎是非法的。为什么呢?
Visual studio(2015 和 2017)似乎允许在 first 示例中使用附加名称空间进行前向声明,但在第二个示例中不是。这是一个错误吗?
示例 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' 的模板参数,需要一个真实类型
是的,这是一个错误。
... 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-specifiertemplate
opt 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,因此上面的段落不适用。综上所述,这个规则是合理的。