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?