4

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.

RammerChoi
  • 99
  • 6
  • What's _range-v3_? And what's it's relation to the c++ standards? – πάντα ῥεῖ May 15 '16 at 13:52
  • 1
    `RANGES_DECLTYPE_AUTO_RETURN` is a macro which puts its argument in a decltype specifier as a return type, so it serves as an expression SFINAE, and the overload taking `long` serves as a second choice fallback – Piotr Skotnicki May 15 '16 at 13:53
  • @πάνταῥεῖ sorry. I acted rashly. range-v3(Ranges) is a one of proposal of c++1y features. you can get the source [here](https://github.com/ericniebler/range-v3) – RammerChoi May 15 '16 at 14:17

3 Answers3

6

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.

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
4
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.

Yakk - Adam Nevraumont
  • 262,606
  • 27
  • 330
  • 524
0

Give the compiler a proper literal?

return {range_access::begin_cursor(derived(), 42l)};

(It is "forty-two el", not the number 421. :))

bipll
  • 11,747
  • 1
  • 18
  • 32