In range-v3, all of the functions are really global function objects in an inline namespace:
#if RANGES_CXX_INLINE_VARIABLES < RANGES_CXX_INLINE_VARIABLES_17 #define RANGES_INLINE_VARIABLE(type, name) \ inline namespace function_objects \ { \ inline namespace \ { \ constexpr auto &name = ::ranges::static_const<type>::value; \ } \ } #else // RANGES_CXX_INLINE_VARIABLES >= RANGES_CXX_INLINE_VARIABLES_17 #define RANGES_INLINE_VARIABLE(type, name) \ inline namespace function_objects \ { \ inline constexpr type name{}; \ } #endif // RANGES_CXX_INLINE_VARIABLES
What is the purpose of the function_objects
namespace? It is not referenced anywhere else in the library as far as I can tell.
Inline namespaces are a library versioning feature akin to symbol versioning, but implemented purely at the C++11 level (ie. cross-platform) instead of being a feature of a specific binary executable format (ie. platform-specific).
Namespaces provide a way of declaring variables within a program that have similar names. It allows users to define functions with the same name as a function in a pre-defined library or used-defined functions within main() . Namespaces can also be used to define classes, variable names, and functions.
Namespaces in C++ You only need to prefix the function you wish to call with namespace_name:: -- similar to how you would call a static member function of a class. Another convenience of namespaces is that they allow you to use the same function name, when it makes sense to do so, to perform multiple different actions.
Namespace is a feature added in C++ and is not present in C. A namespace is a declarative region that provides a scope to the identifiers (names of functions, variables or other user-defined data types) inside it.
Based on Casey's comments on the PR that led to this addition (thanks Justin), the inline namespace
is necessary for something like this to work:
namespace N {
namespace detail {
// default implementation
template <typename T> void swap(T&, T& ) { ... }
struct swap_fn {
template <typename T>
void operator()(T& a, T& b) {
swap(a, b); // unqualified call, the default is in scope
}
};
}
// the object
inline namespace CPO { inline constexpr detail::swap_fn swap{}; } // #1
struct S { friend void swap(S&, S&) { ... } }; // #2
}
N::S a;
N::swap(a, a); // calls the CPO, which calls the friend function
If the customization point object swap
(at #1
) were not in its own namespace, there would be a name clash between the cpo and the non-member friend
declared for type S
(at #2
). Those must be in different namespaces.
Putting the CPO in an inline namespace is sufficient because regular unqualified or qualified lookup will never find the swap at #2
- it will only be found by ADL, which only happens within swap_fn::operator()
.
Yeah, this is pretty cool. And complex.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With