Why does range-v3 put its function objects into an inline namespace?

  • A+
Category:Languages

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.


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.

Comment

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen: