4

There are many similar questions here. All of them asks about usage of std::move in return in specific cases. But I want to know when std::move should be (or shouldn't be) used in return statement in general.

Here I found the following answer:

All return values are already moved or else optimized out, so there is no need to explicitly move with return values.

Compilers are allowed to automatically move the return value (to optimize out the copy), and even optimize out the move!

So I expected that std::move must never be used in return statement, because in any case compiler will optimize that. But I decided to check this and written the following test code:

class A
{
public:
    A() {}
    A(const A  & a) { std::cout << "A(const A  &)" << std::endl; }
    A(      A && a) { std::cout << "A(      A &&)" << std::endl; }
};

A func1(bool first)
{
    A a1, a2;
    return (first ? a1 : a2);
}

int main()
{
    A a1(func1(true));
    return 0;
}

And here is what I got:

A(const A  &)

So compiler did not automatically move the value in return statement, and I had to moved it manually:

return std::move(first ? a1 : a2);

This returned:

A(      A &&)

However when I rewrote my code in such way:

A func2(bool first)
{
    A a1, a2;
    if (first) return a1; else return a2;
}

int main()
{
      A a2(func2(true));
      return 0;
}

I found that automatic move is working:

A(      A &&)

Next test:

A func3(A &&a)
{
    return a;
}

int main()
{
    A a3(func3(A()));
    return 0;
}

Result:

A(const A  &)

So here return std::move(a) must be used.

My understating is next. Compiler automatically moves (or even use RVO) only local variables (and parameters passed by value (they are actually local variables too)). Rvalue references passed as parameters must be moved manually (it's probably because compiler doesn't know whether this external reference will be used anywhere later). Also compiler use move only in “pure” return statement (return var;) and can’t move when returning expression which uses local variables (return <some expression using var>, e.g. return somefunc(var);).

So std::move should not be used in pure "return local variable" statements (using std::move in this case prevents Return Value Optimization). In all other cases std::move should be used in return.

Is my understanding right?

anton_rh
  • 8,226
  • 7
  • 45
  • 73
  • Your understanding isn't quite right – M.M Dec 04 '15 at 12:44
  • @101010 Does that actually answer "Also compiler use move only in “pure” return statement (return var;) and can’t move when returning expression which uses local variables (return , e.g. return somefunc(var);)." from OP's post? – Emil Laine Dec 04 '15 at 12:44
  • 1
    I looked at the supposed duplicate, but this question is a little different. I know C++ well, but not move/rvalue refs inside-and-out. Using the ternary logic operator or an if clause in this case seems semantically very similar, so why do we get different constructors called? – Erik Alapää Dec 04 '15 at 12:45
  • 1
    Read over the list of copy-elision contexts. I guess the rule would be to use `std::move` in a situation that is not a copy-elision context – M.M Dec 04 '15 at 12:45
  • The second-most answer on the duplicate thread makes a good point: in your `func3` you should take the parameter by value if you intend to return it, thereby avoiding this issue – M.M Dec 04 '15 at 12:47
  • @ErikAlapää I think that would be a slightly different question, namely [Why is returning `a ? b : c` not a copy elision context?](http://stackoverflow.com/questions/11914691/copy-elision-move-constructor-not-called-when-using-ternary-expression-in-retur/) – M.M Dec 04 '15 at 12:53
  • @M.M Is "if (first) return a1; else return a2;" copy-elision context? Compiler uses move semantic here. Or do you mean that using std::move where it's done automatically, is ok too. – anton_rh Dec 04 '15 at 12:56
  • Ergh, I was just writing an answer to address each of OP's cases – M.M Dec 04 '15 at 12:56
  • 1
    @M.M Aargh, I like C++ in spite of it being too big and complex. But this kind of language-lawyer stuff is why I advocate do-it-yourself with (preferably smart) pointers instead of relying on std::move and/or return value optimizations. – Erik Alapää Dec 04 '15 at 12:57
  • @anton_rh Yes that is a copy elision context. As explained in the duplicate, returning an lvalue can only move when it is a copy elision context. – M.M Dec 04 '15 at 12:57

0 Answers0