Home > front end >  Why are standard library types accessible inside `std` despite being nested in implementation-define
Why are standard library types accessible inside `std` despite being nested in implementation-define

Time:03-25

I was browsing the implementation of the <optional> header for GCC 11.2 (which can be found here), and I noticed something I'm struggling to understand. Here's the header with (hopefully) only the important bits left out:

#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

I found that _GLIBCXX_BEGIN_NAMESPACE_VERSION and _GLIBCXX_END_NAMESPACE_VERSION expand to namespace __8 { and }, respectively (there's no inline before namespace __8).

Therefore, it looks like std::optional is actually defined inside the non-inline namespace std::__8, but despite that, I can obviously reference std::optional in my programs as if it was located directly within std.

I don't think there are any using directives in effect, first because I haven't found any, and second because it should be allowed to specialize std::optional for custom types without opening implementation-defined namespaces ([namespace.std#2], [temp.spec.partial.general#6]).

The _GLIBCXX_VISIBILITY(default) macro expands to __attribute__ ((__visibility__ ("default"))), but I think it's unrelated (documentation). I couldn't find system_header in the list of pragmas in the documentation.

Therefore, I don't understand the reason why I should be able to reference the optional class as std::optional and not std::__8::optional. What am I missing here?

CodePudding user response:

The config.h in libstdc that defines these macros also initially declares the version namespace as 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
}

And once a namespace is declared inline, it's always inline. For instance:

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

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

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

clang warns on this even if gcc doesn't, but also it's a system header so even if gcc wanred on it it wouldn't matter.

Still, not sure why not just add inline to the macro definition. We're probably not at the point where the additional 7 characters per header is a significant detriment to compile time?

  • Related