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.