The deduced return value conversion operators are a bit strange. But the core idea is that it acts like a function parameter to pick which one is used.
And when deciding between T&&
and T&
the T&
wins in the overload resolution rules. This is to permit:
template<class T>
void f( T&& ) { std::cout << "rvalue"; }
template<class T>
void f( T& ) { std::cout << "lvalue"; }
to work. T&&
can match against an lvalue, but when both the lvalue and universal reference overloads are available, the lvalue one is preferred.
The right set of conversion operators is probably:
template <typename T>
operator T&&() &&;
template <typename T>
operator T &() const; // maybe &
or even
template <typename T>
operator T() &&;
template <typename T>
operator T &() const; // maybe &
to prevent failed lifetime extension from biting you.
3 The types used to determine the ordering depend on the context in which the partial ordering is done:
[SNIP]
(3.2) In the context of a call to a conversion function, the return types of the conversion function templates are used.
Which then ends up depending on "more specialized" rules when picking overloads:
(9.1) if the type from the argument template was an lvalue reference and the type from the parameter template was not, the parameter type is not considered to be at least as specialized as the argument type; otherwise,
Thus operator T&&
is not at least as specialized as operator T&
, meanwhile no rule states operator T&
is not at least as specialized as operator T&&
, so operator T&
is more specialized than operator T&&
.
More specialized templates win overload resolution over less, everything else being equal.