Implicit moving in return statement requiring a conversion operation

186 views Asked by At

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]) or co_­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_­value overload 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?

0

There are 0 answers