GCC 不喜欢与匿名命名空间前向声明交朋友,但 MSVC 喜欢。什么?

Mac*_*cke 10 c++ language-lawyer

GodBolt.org 上测试时,此代码在 MSVC 上编译,但不在 GCC 上编译

Baz 类是在匿名命名空间中声明的,这使得 GCC 认为它与我在下面定义的类不同,但 MSVC 似乎将它们连接起来。

namespace { class Baz; }

class Foo { protected: int x; };
class Bar : public Foo { friend class Baz; };

namespace {
  class Baz { void f() { Bar b; b.x = 42; } };
}
Run Code Online (Sandbox Code Playgroud)

根据标准什么是正确的?

Hum*_*ler 5

这似乎是语言措辞的差异,不同的编译器在这个问题上持不同的立场。MSVC 和 clang 会按原样接受代码,但像 GCC 和 Edge 这样的编译器会拒绝它。

矛盾的措辞来自:

10.3.1.2 [命名空间.memdef]

... 确定实体是否先前已声明的查找不应考虑最内部封闭命名空间之外的任何范围。

该结构体Baz未在最内部的封闭命名空间中声明,但在那里是可见的,因此正常的名称查找会找到它。但是由于这不是正常的名称查找,像 gcc 和 Edge 这样的编译器不会查看封闭的命名空间,只会查看最内部的命名空间。

此信息来自讨论该主题的此归档gcc 错误

似乎 MSVC 和 Edge 选择以不同的方式解释使用匿名命名空间,这会将 OP 的代码转换为以下内容:

namespace unnamed { }
using namespace unnamed;
namespace unnamed { struct Baz; }

class Foo { protected: int x; };
class Bar : public Foo { friend class Baz; };

namespace unnamed { class Baz { void f() { Bar b; b.x = 42; } }; }
Run Code Online (Sandbox Code Playgroud)

这个等效的代码也被 gcc 和 Edge 等编译器拒绝,但被 MSVC 和 clang 接受,因为对是否using考虑通过声明或指令引入的类型进行friend名称查找的解释不同。有关此问题的更多信息,请访问cwg-138