Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 926
RammerChoi Avatar asked Dec 25 '22 05:12

RammerChoi


2 Answers

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.

like image 176
Piotr Skotnicki Avatar answered Mar 01 '23 22:03

Piotr Skotnicki


RANGES_DECLTYPE_AUTO_RETURN
(
  static_cast<Rng const &>(rng).begin_cursor()
)

expands to something like

-> decltype(static_cast<Rng const &>(rng).begin_cursor())
   { return static_cast<Rng const &>(rng).begin_cursor(); }

the two begin_cursor have different ->decltype return values, which gives you SFINAE. Both overloads are considered. If the const version has a SFINAE failure due to the decltype expression being ill-formed in the immediate context, it is eliminated, and the long version is picked.

If it does not, 42 prefers to convert to int instead of long. So the const version is picked.

like image 27
Yakk - Adam Nevraumont Avatar answered Mar 01 '23 23:03

Yakk - Adam Nevraumont