Given the following:
Foo getFoo()
{
Foo result = doSomeWork();
return result;
}
Does C++ guarantee that result
will be moved, instead of copied? Or to put it another way, is writing return std::move(result)
superfluous?
Are there any (other) situations where the standard specifies that a lvalue will be silently moved instead of copied, in the absence of an explicit std::move
cast?
Notes:
Assume Foo
is move-constructible.
Disregarding copy/move elision, which may apply in addition.
Despite the fact that the move might be elided, Yes. A copy will never happen if a move constructor is available. I'll quote the paragraph again for clarity. [class.copy]/32:
When the criteria for elision of a copy/move operation are met, but not for an exception-declaration, and the object to be copied is designated by an lvalue, or when the expression in a
return
statement is a (possibly parenthesized) id-expression that names an object with automatic storage duration 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 is first performed as if the object were designated by an rvalue. If the first overload resolution fails or was not performed, or if the type of the first parameter of the selected constructor is not an rvalue reference to the object’s type (possibly cv-qualified), overload resolution is performed again, considering the object as an lvalue. [Note: This two-stage overload resolution must be performed regardless of whether copy elision will occur. It determines the constructor to be called if elision is not performed, and the selected constructor must be accessible even if the call is elided. — end note ]
Applying std::move
is not superfluous but actually prevents copy elision from being performed, [class.copy]/31:
— in a
return
statement in a function with a class return type, when the expression is the name of a non-volatile automatic object [..]
Yes, one situation - Again I assume you meant that if copy elision isn't performed, a move is done (Copy elision must be applicable if the lvalue is moved, s.a.).
Consider this:
A a;
throw a;
The criteria are met:
— in a throw-expression, when the operand is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one), the copy/move operation from the operand to the exception object (15.1) can be omitted by constructing the automatic object directly into the exception object
Demo.
This is the only other case where an lvalue is moved instead of copied; The other two cases for copy elision solely include temporaries not bound to a reference (which must therefore be designated by prvalues) and the exception-declaration thingy, which is uninteresting here as it covers the exception object we don't see.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With