In C++, when you use std::move on an object, you're not actually converting it into an rvalue reference (T&&). Instead, you're casting it to an rvalue, which signals the compiler that it can treat the object as a temporary and, if possible, move from it instead of copying. However, std::move does not alter the underlying type; it merely provides a hint for the compiler to use move semantics.
Here’s what’s happening in both examples:
Example 1: A&& func(A& rhs)
In this version, the function func returns an A&& (an rvalue reference to A). When you return std::move(rhs), you're directly returning an rvalue reference, which matches the return type A&&. This makes sense and will compile as expected.
Example 2: A func(A& rhs)
In this case, the function func has a return type of A, which is a value (not an rvalue reference). However, when you return std::move(rhs), you're still casting rhs to an rvalue reference (A&&). Here’s why this still compiles and behaves as you observed:
Automatic Conversion: When you specify a return type of A (a value), the compiler expects to create and return an A object. If you provide an A&& (via std::move(rhs)), the compiler interprets this as an instruction to move-construct an A from rhs rather than copy it. The compiler sees that rhs is an rvalue and uses the move constructor to construct the return value.
Return Type Deduction and Temporary Materialization: The compiler will implicitly perform a move when returning an rvalue if the return type is by value. In this case, A func(A& rhs) specifies a return type of A, so the compiler treats std::move(rhs) as an rvalue, creating a temporary A using the move constructor.