Search code examples
c++c++11c++14range-v3

overload resolution from magic number to int or long (in range-v3)


In range-v3, view_facade class has begin() function.

template<typename D = Derived, CONCEPT_REQUIRES_(Same<D, Derived>())>
detail::facade_iterator_t<D> begin()
{
    return {range_access::begin_cursor(derived(), 42)};
}

And the range_access::begin_cursor() is implemented like this,

template<typename Rng>
static RANGES_CXX14_CONSTEXPR auto begin_cursor(Rng & rng, long) // --1
RANGES_DECLTYPE_AUTO_RETURN
(
    rng.begin_cursor()
)
template<typename Rng>
static RANGES_CXX14_CONSTEXPR auto begin_cursor(Rng & rng, int) // --2
RANGES_DECLTYPE_AUTO_RETURN
(
    static_cast<Rng const &>(rng).begin_cursor()
)

In my VS, it looks the second function is always called.

I wonder when the magic number (42) is converted into long to call first function.


Solution

  • Given that RANGES_DECLTYPE_AUTO_RETURN is defined as:

    #define RANGES_DECLTYPE_AUTO_RETURN(...)                        \
        -> decltype(__VA_ARGS__)                                    \
        { return (__VA_ARGS__); }                                   \
        /**/
    

    then your two overloads (after macro expansion) become:

    template<typename Rng>
    static auto begin_cursor(Rng & rng, long) // --1
        -> decltype(rng.begin_cursor())
    {
        return rng.begin_cursor()
    }
    
    template<typename Rng>
    static auto begin_cursor(Rng & rng, int) // --2
        -> decltype(static_cast<Rng const &>(rng).begin_cursor())
    {
        return static_cast<Rng const &>(rng).begin_cursor();
    }
    

    When calling begin_cursor with an int argument, overload resolution finds an exact match which is the second overload. It is preferred to long, as this one requires a conversion of the argument expression. However, if static_cast<Rng const &>(rng).begin_cursor() is invalid, i.e., when member function begin_cursor() is not const-qualified, the expression inside the decltype specifier will trigger a substitution failure, therefore, the compiler will continue to search for another overload, falling back to the first function.