Moving a Return Value is Easy

Summary

In this post, we will discuss rvalue references and move semantics in C++11.

Details

Rvalue references and move semantics can be very complex, see this article. But the truth is that: the simplest is the best. It starts with three examples of returning values from functions. Read them and ask yourself which of them is going to do no vector copy.

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();

It returns a temporary caught by rval_ref whose life extended beyond the rval_ref definition. We can use it as if you had caught it by value.

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();

Actually it will cause a run time error. Because rval_ref now holds a reference to the destructed tmp inside the function. The following codes crashes.

std::vector<int> &&rval_ref = return_vector();
cout << rval_ref[0] << endl;
rval_ref[0] = 2;
cout << rval_ref[0] << endl; // Process finished with exit code 11

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();

This example is roughly equivalent to the first. Actually the std::move on tmp is unnecessary and even can be inhibit return value optimization.

Best way

The following simple code is the best way to get value from functions.

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

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

It will actually do
1. RVO (no copy, no move) by default;
2. move constructor if RVO is not possible;
3. copy constructor if RVO is not possible and the returned type did not have a move constructor.
Thanks to the compilers, “often the cleanest, simplest code that doesn’t even mention move or && anywhere is just what you want – that’s C++11, clean, safe, and faster than ever”.

RVO

Return value optimization, aka RVO, is a compiler optimization technique that allows the compiler to construct the return value of a function at the call site. The technique is also named “elision”. C++98/03 standard doesn’t require the compiler to provide RVO optimization, but most popular C++ compilers contain this optimization technique, such as IBM XL C++ compiler, GCC and Clang . This optimization technique is included in the C++11 standard due to its prevalence. As defined in Section 12.8 in the C++11 standard, the name of the technique is “copy elision”.

Reference

  1. isocpp.org
  2. Q&A in stack-overflow

Leave a Reply

Your email address will not be published. Required fields are marked *