It is known that std::move
should not be applied to the function return values because it can prevent RVO (return value optimization). I am interested in the question what should we do if we certainly know that RVO will not happen.
This is what the C++14 standard says [12.8/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 ]
Here is the explanation from the book Effective Modern C++
The part of the Standard blessing the RVO goes on to say that if the conditions for the RVO are met, but compilers choose not to perform copy elision, the object being returned must be treated as an rvalue. In effect, the Standard requires that when the RVO is permitted, either copy elision takes place or std::move is implicitly applied to local objects being returned
As I understand when return object can't be elided at first it should be regarded as rvalue
. In these example we can see that when we pass argument greater than 5
object is moved otherwise it is copied. Does it mean that we should explicitly write std::move
when we know that RVO will not happen?
#include <iostream>
#include <string>
struct Test
{
Test() {}
Test(const Test& other)
{
std::cout << "Test(const Test&)" << std::endl;
}
Test(Test&& other)
{
std::cout << "Test(const Test&&)" << std::endl;
}
};
Test foo(int param)
{
Test test1;
Test test2;
return param > 5 ? std::move(test1) : test2;
}
int main()
{
Test res = foo(2);
}
The output of this program is Test(const Test&)
.
rvalue
" As long as the conditions for copy elision are met (or they would be met except the object is a function parameter.) – Impressmentstd::move
on return values but in this example it would be copied it I not write it. It I change?
operator toif
it would always moved... – Confirmandreturn std::move(param > 5 ? test1 : test2);
? (since test1 and test2 are of the same type, the result of that ternary expression is a reference, which is not the case in your example) – Irenareturn std::move(...)
is pointless. – Rixdollar