This question already has an answer here:
In this case
struct Foo {};
Foo meh() {
return std::move(Foo());
}
I'm pretty sure that the move is unnecessary, because the newly created Foo
will be an xvalue.
But what in cases like these?
struct Foo {};
Foo meh() {
Foo foo;
//do something, but knowing that foo can safely be disposed of
//but does the compiler necessarily know it?
//we may have references/pointers to foo. how could the compiler know?
return std::move(foo); //so here the move is needed, right?
}
There the move is needed, I suppose?
The move is unnecessary in both cases. In the second case,
std::move
is superfluous because you are returning a local variable by value, and the compiler will understand that since you're not going to use that local variable anymore, it can be moved from rather than being copied.There are lots of answers about when it shouldn't be moved, but the question is "when should it be moved?"
Here is a contrived example of when it should be used:
ie, when you have a function that takes an rvalue reference, modifies it, and then returns a copy of it. Now, in practice, this design is almost always better:
which also allows you to take non-rvalue parameters.
Basically, if you have an rvalue reference within a function that you want to return by moving, you have to call
std::move
. If you have a local variable (be it a parameter or not), returning it implicitlymove
s (and this implicit move can be elided away, while an explicit move cannot). If you have a function or operation that takes local variables, and returns a reference to said local variable, you have tostd::move
to get move to occur (as an example, the trinary?:
operator).On a return value, if the return expression refers directly to the name of a local lvalue (i.e. at this point an xvalue) there is no need for the
std::move
. On the other hand, if the return expression is not the identifier, it will not be moved automatically, so for example, you would need the explicitstd::move
in this case:When returning a named local variable or a temporary expression directly, you should avoid the explicit
std::move
. The compiler must (and will in the future) move automatically in those cases, and addingstd::move
might affect other optimizations.A C++ compiler is free to use
std::move(foo)
:foo
is at the end of its lifetime, andstd::move
won't have any effect on the semantics of the C++ code other than the semantic effects allowed by the C++ specification.It depends on the optimization capabilities of the C++ compiler whether it is able to compute which transformations from
f(foo); foo.~Foo();
tof(std::move(foo)); foo.~Foo();
are profitable in terms of performance or in terms of memory consumption, while adhering to the C++ specification rules.Conceptually speaking, year-2017 C++ compilers, such as GCC 6.3.0, are able to optimize this code:
into this code:
which avoids calling the copy-constructor and the destructor of
Foo
.Year-2017 C++ compilers, such as GCC 6.3.0, are unable to optimize these codes:
into these codes:
which means that a year-2017 programmer needs to specify such optimizations explicitly.
In the case of
return std::move(foo);
themove
is superfluous because of 12.8/32:return foo;
is a case of NRVO, so copy elision is permitted.foo
is an lvalue. So the constructor selected for the "copy" fromfoo
to the return value ofmeh
is required to be the move constructor if one exists.Adding
move
does have a potential effect, though: it prevents the move being elided, becausereturn std::move(foo);
is not eligible for NRVO.As far as I know, 12.8/32 lays out the only conditions under which a copy from an lvalue can be replaced by a move. The compiler is not permitted in general to detect that an lvalue is unused after the copy (using DFA, say), and make the change on its own initiative. I'm assuming here that there's an observable difference between the two -- if the observable behavior is the same then the "as-if" rule applies.
So, to answer the question in the title, use
std::move
on a return value when you want it to be moved and it would not get moved anyway. That is:Considering that this is quite fiddly and moves are usually cheap, you might like to say that in non-template code you can simplify this a bit. Use
std::move
when:By following the simplified rules you sacrifice some move elision. For types like
std::vector
that are cheap to move you'll probably never notice (and if you do notice you can optimize). For types likestd::array
that are expensive to move, or for templates where you have no idea whether moves are cheap or not, you're more likely to be bothered worrying about it.std::move
is totally unnecessary when returning from a function, and really gets into the realm of you -- the programmer -- trying to babysit things that you should leave to the compiler.What happens when you
std::move
something out of a function that isn't a variable local to that function? You can say that you'll never write code like that, but what happens if you write code that's just fine, and then refactor it and absent-mindedly don't change thestd::move
. You'll have fun tracking that bug down.The compiler, on the other hand, is mostly incapable of making these kinds of mistakes.
Also: Important to note that returning a local variable from a function does not necessarily create an rvalue or use move semantics.
See here.