在 libc++ 的内联命名空间中前向声明类的可移植方式是啥?

Posted

技术标签:

【中文标题】在 libc++ 的内联命名空间中前向声明类的可移植方式是啥?【英文标题】:What is the portable way to forward-declare classes in an inline namespace in libc++?在 libc++ 的内联命名空间中前向声明类的可移植方式是什么? 【发布时间】:2016-06-14 17:54:47 【问题描述】:

只有当我将-stdlib=libc++ 指定为clang++ 时,以下代码才会编译:

namespace std 
  class mutex;


void f(std::mutex &x);

#include <mutex>

void f(std::mutex &x)  
http://goo.gl/Uqpv6f (gcc.godbolt.org)

注意:通过名称查找找到的候选人是 'std::__1::mutex'

我明白那里的::__1 是什么,

Where does the __1 symbol come from when using LLVM's libc++?

但在我看来,libc++ 破坏了 C++ 标准定义的 API:

http://en.cppreference.com/w/cpp/thread/mutex(好吧,这不是一个真正的标准......)

应该可以转发声明 std::mutex,因为它应该直接位于 std 下,不是吗?

请注意,编译阶段而不是链接阶段失败了。所以我认为我的问题的答案不应该是“因为 libc++ 使用了与 GNU libstdc++ 不同的 ABI...”

【问题讨论】:

【参考方案1】:

但在我看来,libc++ 打破了 C++ 标准定义的 API:

其实不然。该标准在[内容]中规定:

未指定名称是否在特定的 命名空间直接在该命名空间中或在该命名空间内的内联命名空间中声明。

libc++ 允许将mutex 放入inline namespace __1。请注意,good reasons 需要内联命名空间,通常作为用户,您甚至都不关心它们是否存在。

对于您的具体问题,您可以仍然使用 libc++ 向前声明 std::mutex...您只需包含所有命名空间(请参阅 this question 了解如何检测 -std=libc++ ):

namespace std 
#ifdef _LIBCPP_VERSION
    inline namespace __1 
        struct mutex;
    
#else
    struct mutex;
#endif

但是,来自 [namespace.std]:

如果 C++ 程序将声明或定义添加到命名空间 std 或 除非另有说明,否则命名空间 std 内的命名空间。

前向声明仍然是一个声明,所以即使是上面的解释内联命名空间的版本也是未定义的行为。所以更喜欢直接做:

#include <mutex>

【讨论】:

感谢您引用允许 libc++ 破坏我的代码的标准。好吧,我有点期待它写在标准的某个地方......我想我需要像#define _STD_BEGIN ...这样的东西 std添加声明,甚至是前向声明都是UB。 @nodakai 从技术上讲,libc++ 并没有破坏您的代码。你的代码被破坏了。它只是碰巧起作用了。 @T.C.谢谢,很高兴知道。您可以将其发布为答案,我会接受它

以上是关于在 libc++ 的内联命名空间中前向声明类的可移植方式是啥?的主要内容,如果未能解决你的问题,请参考以下文章

在 Objective-C 中前向声明枚举

C编程中前向声明的意义是啥?

在另一个函数中前向声明“constexpr”函数——编译器错误?

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

为啥内联未命名的命名空间?

为啥 C++ 友元类只需要在其他命名空间中进行前向声明?