In C++20 there is a rule that informally known as "when returning a variable, first try to move, and only then copy".
More formally it is [class.copy.elision]/3
An implicitly movable entity is a variable of automatic storage duration that is either a non-volatile object or an rvalue reference to a non-volatile object type. In the following copy-initialization contexts, a move operation might be used instead of a copy operation:
- If the expression in a
return([stmt.return])orco_return([stmt.return.coroutine])statement is a (possibly parenthesized) id-expression that names an implicitly movable entity declared in the body or parameter-declaration-clause of the innermost enclosing function or lambda-expression, ...overload resolution to select the constructor for the copy or the
return_valueoverload to call is first performed as if the expression or operand were an rvalue. ...
Let us consider function template
template<typename T>
T f() {
struct {
T x;
operator T&&() && { return static_cast<T&&>(x); }
} s;
return s;
}
which returns a variable of automatic storage duration s. And there is a conversion operator taking rvalue-reference && that can be used to construct function return value of type T.
I expected that thanks to above-mentioned rule, it will work for any type T. However it did not:
struct A{};
int main() {
// ok in GCC and Clang
f<A>();
// error everywhere
//f<int>();
}
Calling f with template argument struct A works in Clang and GCC, but not in MSVC printing the error:
error C2440: 'return': cannot convert from 'f::<unnamed-type-s>' to 'T' with [T=A]
note: No user-defined-conversion operator available that can perform this conversion, or the operator cannot be called
Does MSVC have some bug here?
And calling f with template argument int does not work anywhere. MSVC's error is the same, and Clang's error now:
error: no viable conversion from returned value of type 'struct (unnamed struct at <source>:3:5)' to function return type 'int'
note: candidate function not viable: expects an rvalue for object argument
operator T&&() && { return static_cast<T&&>(x); }
Online demo: https://gcc.godbolt.org/z/dfdjEx4v4
Is it expected that the rule works when the function returns class types like A and not scalar types like int?