Search code examples
c++returnmove-semantics

In C++, what does it mean to use a move operation on return?


I'm reading through Bjarne Stroustrup's The C++ Programming Language (4th edition) and on p. 516 he says:

How does the compiler know when it can use a move operation rather than a copy operation? In a few cases, such as for a return value, the language rules say that it can (because the next action is defined to destroy the element)

Also on p. 517 he says:

[The object] has a move constructor so that "return by value" is simple and effecient as well as "natural"

If return always uses a move operation, then why doesn't something like the following work?

#include <vector>
#include <assert.h>

using namespace std;

vector<int> ident(vector<int>& v) {
    return v;
};

int main() {
    vector<int> a {};
    const vector<int>& b = ident(a);
    a.push_back(1);
    assert(a.size() == b.size());
}

Shouldn't a and b be be pointing to the same objects?


Solution

  • Quoting from http://eel.is/c++draft/class.copy.elision:

    In the following copy-initialization contexts, a move operation might be used instead of a copy operation:

    (3.1) If the expression in a return statement ([stmt.return]) 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, or

    (3.2) if the operand of a throw-expression is the name of a non-volatile automatic object (other than a function or catch-clause parameter) whose scope does not extend beyond the end of the innermost enclosing try-block (if there is one),

    overload resolution to select the constructor for the copy is first performed as if the object were designated by an rvalue.

    Consequently, if you return an automatic (local) variable:

    vector<int> ident() {
      vector<int> v;
      return v;
    };
    

    then, v will be in return statement treated as an rvalue and therefore, the returned value will initialized be move constructor.

    However, these criteria are not met in your code, since v in your ident is not an automatic variable. Therefore, it is treated as an lvalue in the return statement and the return value is initiliazed by copy constructor from the vector referenced by the function parameter.

    These rules are quite natural. Imagine that compilers were allowed to move from all lvalues in return statements. Fortunately, they can only if they know that that lvalue is going to be destroyed, which holds for automatic variables and parameters passed by values in the context of return statements.