请考虑
namespace M {
void f();
struct S;
}
namespace N {
void M::f() {}
struct M::S {};
}
这段代码被我测试的所有编译器都拒绝了,因为M::f
和M::S
只允许在封闭作用域中定义。标准还包含一个示例,暗示函数只能在封闭作用域中定义。
然而,我找不到标准中实际规则的具体位置。在C++20中有[class.pre]/3:
如果一个类头名包含一个嵌套名指示符,那么类说明符应该引用之前在该嵌套名指示符所指的类或命名空间中直接声明的类,或者在该命名空间的内联命名空间集合(9.8.2)的元素中声明的类(即不仅仅是通过using-declaration继承或引入的类),并且类说明符应该出现在封闭前一声明的命名空间中。在这种情况下,定义的类头名的嵌套名指示符不得以decltype-specifier开头。成员函数也有一条规则,但我找不到自由函数的规则。P1787R6删除了上述显示的措辞,但替代措辞似乎没有暗示相同的限制。以下是C++23草案中[class.pre]的内容:
如果一个类头名称包含一个嵌套名指示符,那么类说明符不应该位于类作用域中。如果其类名是一个标识符,那么类说明符应该对应于在嵌套名指示符引用的类、类模板或命名空间中可命名的一个或多个声明;它们都应该具有相同的目标作用域,而类说明符的目标作用域就是该作用域。[...]
在上面的例子中,试图定义M::S
时的嵌套名指示符M::
表示命名空间M
,并且此时S
在M
中是可命名的,因为S
之前已经在M
中声明过;参见[basic.scope.scope]/6。
由于我在C++23草案中找不到任何禁止非封闭作用域中定义的规则,我可能认为P1787R6使其成为了允许的。然而,由于编译器仍然不接受这段代码,我认为更有可能的是该规则仍然存在,但可能在我忽略的某个地方(或者措辞比以前更加微妙)。