13

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • Probably has to do something with linkage, just like libc++'s `std::__1` namespace. – Rakete1111 Apr 24 '18 at 20:18
  • 4
    [This is the PR](https://github.com/ericniebler/range-v3/pull/423) that added this code. Maybe the [comments that CaseyCarter made](https://github.com/ericniebler/range-v3/pull/423#issuecomment-230644140) can shed some light on this – Justin Apr 24 '18 at 20:43

1 Answers1

12

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.

Barry
  • 286,269
  • 29
  • 621
  • 977
  • 4
    There's some discussion [on the Core reflector](http://lists.isocpp.org/core/2018/04/4164.php) from when I asked if this was legit after convincing myself we were breaking [\[basic.scope.declarative\]/4](http://eel.is/c++draft/basic.scope.declarative#4). (TLDR: the inline namespace makes it legit.) – Casey Apr 24 '18 at 23:03