When should std::move be used on a function return value?
In the case of return std::move(foo);
the move
is superfluous because of 12.8/32:
When the criteria for elision of a copy operation are met or would be
met save for the fact that the source object is a function parameter,
and the object to be copied is designated by an lvalue, overload
resolution to select the constructor for the copy is first performed as
if the object were designated by an rvalue.
return foo;
is a case of NRVO, so copy elision is permitted. foo
is an lvalue. So the constructor selected for the "copy" from foo
to the return value of meh
is required to be the move constructor if one exists.
Adding move
does have a potential effect, though: it prevents the move being elided, because return 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:
- you want it to be moved, and
- it is an lvalue, and
- it is not eligible for copy elision, and
- it is not the name of a by-value function parameter.
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:
- you want it to be moved, and
- it is an lvalue, and
- you can't be bothered worrying about it.
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 like std::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.
return std::move(m_field) or return m_field?
If you were to return an object with automatic storage, then you should never return with std::move
because former is faster in best case, and equal in worst case.
I have read in some others posts that it's unecessarily and even deoptimize mecanisms like NRVO.
And this is the reason. This doesn't however apply to the function in the question, because it doesn't return an object with automatic storage, but rather a member variable. NRVO cannot apply to a member variable, nor does the implicit move of return value.
So, in your example, return std::move(result);
does a move, and return result;
does a copy. You should use move if the purpose of the function is to move from the member, and copy if the purpose is to copy the member. Note however that moving from a member in a function that is not rvalue qualified is quite unconventional design, and I recommend to avoid it unless you have a good reason to do so. Also note that the copying version can be const qualified, which would make it more generally useful.
P.S. for int
that is used in the example, these distinctions are irrelevant. Moving versus coping is only relevant to non-trivially copyable / -movable types.
when to use move in function calls
When a function accepts an rvalue reference, you have to provide an rvalue (either by having already a prvalue, or using std::move
to create an xvalue). E.g.
void foo(std::string&& s);
std::string s;
foo(s); // Compile-time error
foo(std::move(s)); // OK
foo(std::string{}) // OK
When a function accepts a value, you can use std::move
to move-construct the function argument instead of copy-constructing. E.g.
void bar(std::string s);
std::string s;
bar(s); // Copies into `s`
bar(std::move(s)); // Moves into `s`
When a function accepts a forwarding reference, you can use std::move
to allows the function to move the object further down the call stack. E.g.
template <typename T>
void pipe(T&& x)
{
sink(std::forward<T>(x));
}
std::string s;
pipe(s); // `std::forward` will do nothing
pipe(std::move(s)); // `std::forward` will move
pipe(std::string{}); // `std::forward` will move
Should I move on return when variables are not local
I read a lot of people saying to not do move on return. From what I gather it's because with copy elision, on case get1, the compiler will not call constructor + move constructor but rather just one call to the default constructor, while case get1bad forces the compiler to call constructor + move.
If the operand to a return
statement is just the name of a local variable, then std::move
is basically implicitly applied. So at best return std::move(local_var);
is redundant. This is a special rule for return
. It doesn't apply elsewhere.
However, using return std::move(local_var);
makes it so that the mandatory copy elision rules do not apply anymore, which is why it is not only redundant, but even worse than just return local_var;
.
My question is regarding cases where the variable is not local (get2 vs get3). In that case, the variable is constructed anyway in the class. In get2 there's not really any optimization that I can see being possible. In this case, if I don't really care about ensuring class B has a valid A object, wouldn't it be better to actually move? Wouldn't it just call the move constructor which is generally cheaper than the copy constructor?
In your examples all member functions are const
qualified. Therefore the type of std::move(member_var)
will be const A&&
. The move constructor takes a A&&
, not const A&&
. Therefore no move will happen and the copy constructor will be used for both get2
and get3
's return values.
If you remove the const
qualifiers, then yes return std::move(member_var);
makes sense in that case and allows a move construction rather than copy construction. Copy/move elision is not possible in either case, since variable is not local to the function.
It is unlikely that you wouldn't care about the state of member_var
after calling a member function, so it is unlikely that this will make sense in practice. If at all I would qualify the member function with &&
, so that it is only used if the class instance is an rvalue, which is the only case I can think of where one doesn't care about the object state after the call to the member function.
C++: should I explicitly use std::move() in a return statement to force a move?
You should prefer
std::vector<int> foo() {
std::vector<int> v(100000,1);
return v; // move or NRVO
}
over
std::vector<int> foo() {
std::vector<int> v(100000,1);
return std::move(v); // move
}
The second snippet prevent NRVO, and in worst case both would move construct.
Using std::move() when returning a value from a function to avoid to copy
No. Whenever a local variable in a return
statement is eligible for copy elision, it binds to an rvalue reference, and thus return t;
is identical to return std::move(t);
in your example with respect to which constructors are eligible.
Note however that return std::move(t);
prevents the compiler from exercising copy elision, while return t
; does not, and thus the latter is the preferred style. [Thanks to @Johannes for the correction.] If copy elision happens, the question of whether or not move construction is used becomes a moot point.
See 12.8(31, 32) in the standard.
Note also that if T
has an accessible copy- but a deleted move-constructor, then return t;
will not compile, because the move constructor must be considered first; you'd have to say something to the effect of return static_cast<T&>(t);
to make it work:
T f()
{
T t;
return t; // most likely elided entirely
return std::move(t); // uses T::T(T &&) if defined; error if deleted or inaccessible
return static_cast<T&>(t) // uses T::T(T const &)
}
Should I use std::move on functions returning std::vector?
Neither:
There's no benefit.
The compiler does nothing special except for compiling C++. It will probably try to do this well.
Fortunately, the expression generateVector()
is already an rvalue, so v
will be constructed with the move constructor, but in fact your declaration of v
and your definition of generateVector
are subject to copy elision, so that the construction may be treated as equivalent to:
std::vector<int> v(10, 0);
In other words, the language rules explicitly allow for copy constructors not to be called under these circumstances, even if they have side effects.
Related Topics
Recommended Way to Initialize Srand
How to Use Boost in Visual Studio 2010
Legality of Cow Std::String Implementation in C++11
How to Properly Delete Nodes of Linked List in C++
C++ Cross-Platform High-Resolution Timer
Cmake Error At Cmakelists.Txt:30 (Project): No Cmake_C_Compiler Could Be Found
Strptime() Equivalent on Windows
Post-Increment and Pre-Increment Within a 'For' Loop Produce Same Output
What Happens If I Assign a Negative Value to an Unsigned Variable
How to Iterate Over Cin Line by Line in C++
Difference of Keywords 'Typename' and 'Class' in Templates
What Is Iaca and How to Use It
Why Copying Stringstream Is Not Allowed
How to Make a Fully Statically Linked .Exe With Visual Studio Express 2005