尽管标准库类型嵌套在 implementation-defined 命名空间中,但为什么可以在 `std` 中访问标准库类型?

Why are standard library types accessible inside `std` despite being nested in implementation-defined namespaces?

我正在浏览 GCC 11.2 的 <optional> header 的实现(可以找到 here),我注意到一些我难以理解的东西。这是 header,(希望如此)只遗漏了重要的部分:

#ifndef _GLIBCXX_OPTIONAL
#define _GLIBCXX_OPTIONAL 1

#pragma GCC system_header

#if __cplusplus >= 201703L

/* Includes of various internal and library headers */

namespace std _GLIBCXX_VISIBILITY(default)
{
_GLIBCXX_BEGIN_NAMESPACE_VERSION

#if __cplusplus == 201703L
# define __cpp_lib_optional 201606L
#else
# define __cpp_lib_optional 202106L
#endif

  /* Implementation */

  template<typename _Tp>
  class optional;

  /* Implementation */

  template<typename _Tp>
  class optional: /* Implementation */
  { /* Implementation */ };

  /* Implementation */


_GLIBCXX_END_NAMESPACE_VERSION
} // namespace std

#endif // C++17

#endif // _GLIBCXX_OPTIONAL

我发现_GLIBCXX_BEGIN_NAMESPACE_VERSION_GLIBCXX_END_NAMESPACE_VERSION分别扩展为namespace __8 {}namespace __8之前没有inline)。

因此,看起来 std::optional 实际上是在 non-inline 命名空间 std::__8 中定义的,但尽管如此,我显然可以在我的程序中引用 std::optional,就好像它直接位于 std.

我认为没有任何有效的 using 指令,首先是因为我还没有找到任何指令,其次是因为应该允许它在不打开的情况下专门针对自定义类型 std::optional implementation-defined 个命名空间 ([namespace.std#2], [temp.spec.partial.general#6])。

_GLIBCXX_VISIBILITY(default) 宏扩展为 __attribute__ ((__visibility__ ("default"))),但我认为它不相关 (documentation)。我在文档的编译指示列表中找不到 system_header

因此,我不明白为什么我应该能够将可选的 class 引用为 std::optional 而不是 std::__8::optional。我在这里错过了什么?

定义这些宏的libstdc++中的config.h最初也将版本命名空间声明为inline here:

// Defined if inline namespaces are used for versioning.
#define _GLIBCXX_INLINE_VERSION 

// Inline namespace for symbol versioning.
#if _GLIBCXX_INLINE_VERSION
# define _GLIBCXX_BEGIN_NAMESPACE_VERSION namespace __8 {
# define _GLIBCXX_END_NAMESPACE_VERSION }

namespace std
{
inline _GLIBCXX_BEGIN_NAMESPACE_VERSION
#if __cplusplus >= 201402L
  inline namespace literals {
    inline namespace chrono_literals { }
    inline namespace complex_literals { }
    inline namespace string_literals { }
#if __cplusplus > 201402L
    inline namespace string_view_literals { }
#endif // C++17
  }
#endif // C++14
_GLIBCXX_END_NAMESPACE_VERSION
}

一旦 namespace 被声明 inline,它总是 inline。例如:

namespace A {
  inline namespace B {
    struct X { };
  }
}

namespace A {
  namespace B {
    struct Y { };
  }
}

A::X x; // ok
A::Y y; // still ok

clang 对此发出警告,即使 gcc 没有警告,但它也是一个系统 header 所以即使 gcc 警告它也没关系。

仍然不确定为什么不直接将 inline 添加到宏定义中。我们可能还没有达到每个 header 额外的 7 个字符对编译时间造成重大损害的地步?