如何解决GCC不喜欢使用匿名名称空间的转发声明来结交朋友,但是MSVC愿意什么?
在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; } };
}
根据标准,什么是正确的?
解决方法
这似乎是语言措辞上的差异,不同的编译器对此问题持不同立场。 MSVC和clang将按原样接受代码,但GCC和Edge等编译器会拒绝它。
矛盾的措辞来自:
10.3.1.2 [namespace.memdef]
...用于确定该实体先前是否已声明的查询不应考虑最内层封闭名称空间之外的任何范围。
结构Baz
并未在最内层的命名空间中声明,但在此处可见,因此可以通过常规名称查找找到它。但是,由于这不是正常的名称查找,因此gcc和Edge之类的编译器不会只关注最里面的名称空间。
此信息来自讨论该主题的gcc bug档案。
似乎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; } }; }
gcc和Edge之类的编译器也拒绝了该等效代码,但由于对于{{1}是否考虑通过using
声明或指令引入的类型的解释不同,MSVC和clang也接受了该等效代码}名称查找。有关此问题的更多信息,请访问cwg-138
问题在于,您在友谊声明中使用了详细的类型说明符,而GCC使用它在全局名称空间中声明了类Baz
。 An elaborated type specifier is a declaration unless a previous declaration is found in the inner most enclosing namespace。显然尚不清楚Baz
的声明是否应视为在全局命名空间中。
要解决此问题,只需在好友声明中使用类的名称即可:
namespace { class Baz; }
class Foo { protected: int x; };
class Bar : public Foo { friend Baz; };
namespace {
class Baz { void f() { Bar b; b.x = 42; } };
}
在朋友声明中使用精致的类型说明符是一种习惯性的病理习惯。除非类型名称也是变量名称,否则没有理由使用详细的类型说明符。
,匿名名称空间的作用就好像它们具有唯一名称一样,仅可用于当前的翻译单元。
似乎有些编译器会为翻译单元中的所有匿名命名空间赋予相同的名称,而其他编译器可能不会(只是对可能的实现方式进行猜测),但似乎不值得您依赖。
有关匿名名称空间的更多详细信息可以在这里找到: https://en.cppreference.com/w/cpp/language/namespace#Unnamed_namespaces
版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。