C++11 Rvalues and Move Semantics Confusion (Return Statement)

C++11 rvalues and move semantics confusion (return statement)

First example

std::vector<int> return_vector(void)
{
std::vector<int> tmp {1,2,3,4,5};
return tmp;
}

std::vector<int> &&rval_ref = return_vector();

The first example returns a temporary which is caught by rval_ref. That temporary will have its life extended beyond the rval_ref definition and you can use it as if you had caught it by value. This is very similar to the following:

const std::vector<int>& rval_ref = return_vector();

except that in my rewrite you obviously can't use rval_ref in a non-const manner.

Second example

std::vector<int>&& return_vector(void)
{
std::vector<int> tmp {1,2,3,4,5};
return std::move(tmp);
}

std::vector<int> &&rval_ref = return_vector();

In the second example you have created a run time error. rval_ref now holds a reference to the destructed tmp inside the function. With any luck, this code would immediately crash.

Third example

std::vector<int> return_vector(void)
{
std::vector<int> tmp {1,2,3,4,5};
return std::move(tmp);
}

std::vector<int> &&rval_ref = return_vector();

Your third example is roughly equivalent to your first. The std::move on tmp is unnecessary and can actually be a performance pessimization as it will inhibit return value optimization.

The best way to code what you're doing is:

Best practice

std::vector<int> return_vector(void)
{
std::vector<int> tmp {1,2,3,4,5};
return tmp;
}

std::vector<int> rval_ref = return_vector();

I.e. just as you would in C++03. tmp is implicitly treated as an rvalue in the return statement. It will either be returned via return-value-optimization (no copy, no move), or if the compiler decides it can not perform RVO, then it will use vector's move constructor to do the return. Only if RVO is not performed, and if the returned type did not have a move constructor would the copy constructor be used for the return.

c++11 Return value optimization or move? [duplicate]

Use exclusively the first method:

Foo f()
{
Foo result;
mangle(result);
return result;
}

This will already allow the use of the move constructor, if one is available. In fact, a local variable can bind to an rvalue reference in a return statement precisely when copy elision is allowed.

Your second version actively prohibits copy elision. The first version is universally better.

move semantics in constructor in return statement

This one is more efficient:

std::pair<std::vector<double>,std::vector<double> > some_func() { 
std::vector<double> my_vec_a;
std::vector<double> my_vec_b;
//do something
return std::make_pair(std::move(my_vec_a),std::move(my_vec_b));
}

without the calls to std::move the two vectors will be copied.

The problem, however, isn't the actual return. It's that the two vectors in the pair being constructed are being constructed from lvalues; the compiler can't choose to use a move-constructor there without you explicitly telling it to via std::move.

move constructor called on return instead of copy

From cppreference (emphasis mine):

If [the returned expression] is an lvalue expression and the conditions for copy elision are met, or would be met, except that [the returned expression] names a function parameter, then overload resolution to select the constructor to use for initialization of the returned value is performed twice: first as if [the returned expression] were an rvalue expression (thus it may select the move constructor or a copy constructor taking reference to const), and if no suitable conversion is available, overload resolution is performed the second time, with lvalue [returned expression] (so it may select the copy constructor taking a reference to non-const).
The above rule applies even if the function return type is different from the type of [the returned expression] (copy elision requires same type)

Long story short, return implicitly tries to move what you return when it makes sense. It will only copy as a last resort (no move constructor available, for example).

Why is using the move constructor in a return statement legal?

C++11 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.

Returning a local automatic variable meets the criteria for elision; treating it as if it were an rvalue selects the move constructor.



Related Topics



Leave a reply



Submit