Home > database >  question about implementation of add_rvalue_reference
question about implementation of add_rvalue_reference

Time:08-20

Implementation of add_rvalue_reference in cppreference is the following. What is the need for the int argument (i.e. 0) vs. no argument ?

namespace detail {
 
template <class T>
struct type_identity { using type = T; }; // or use std::type_identity (since C  20)
 
template <class T>
auto try_add_rvalue_reference(int) -> type_identity<T&&>;
template <class T>
auto try_add_rvalue_reference(...) -> type_identity<T>;
 
} // namespace detail
 
 
template <class T>
struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>(0)) {};

CodePudding user response:

If the implementation was like this:

namespace detail {
 
template <class T>
struct type_identity { using type = T; }; // or use std::type_identity (since C  20)
 
template <class T>
auto try_add_rvalue_reference() -> type_identity<T&&>;
template <class T>
auto try_add_rvalue_reference() -> type_identity<T>;
 
} // namespace detail
 
 
template <class T>
struct add_rvalue_reference : decltype(detail::try_add_rvalue_reference<T>()) {};

Then with e.g. T an object type both T and T&& are valid types and so neither of the two overloads will SFINAE for the call detail::try_add_rvalue_reference<T>(). Both overloads will be viable. But now because there is no function parameter/argument pair distinguishing them anymore, both will be equally good in overload resolution with no tiebreaker applying and so the call detail::try_add_rvalue_reference<T>() will be ambiguous.

With int and ... as parameter, the int overload will always be considered a better candidate in overload resolution if both are viable. And the intention is that T&& is chosen whenever that is a valid type.

CodePudding user response:

This implementation utilizes SFINAE and relies on overload resolution for the case where substitution does not fail. In order for overload resolution to choose between viable functions, there must be at least one parameter. The type of the parameter is arbitrary; int is convenient.

The first overload

template <class T>
auto try_add_rvalue_reference(int) -> type_identity<T&&>;

does not always exist. If substituting T into the return type fails, it is not an error. Instead of an error, this template is skipped, producing no function for that particular T.

The second overload

template <class T>
auto try_add_rvalue_reference(...) -> type_identity<T>;

does always exist, but it is a universal fallback. That is, when it comes to overload resolution, it loses to just about everything provided that there is an argument. (When no argument is provided, (...) ties with () in overload resolution.)

If only the second overload exists, then calling the function will invoke that overload (because there is no other choice). Thus, the declared type of the returned value is T. For this case, the existence of a parameter does not matter.

If both overloads exist, then calling the function with an int argument will invoke the first overload (because the second loses to the first in overload resolution). Thus, the declared type of the returned value is T&&. If there was no parameter, then overload resolution would not be able to choose between the two overloads, and compilation would fail.

Substitution Failure Is Not An Error, but overload resolution failure is.

  • Related