135

In this case

struct Foo {};
Foo meh() {
  return std::move(Foo());
}

I'm pretty sure that the move is unnecessary, because the newly created Foo will be an xvalue.

But what in cases like these?

struct Foo {};
Foo meh() {
  Foo foo;
  //do something, but knowing that foo can safely be disposed of
  //but does the compiler necessarily know it?
  //we may have references/pointers to foo. how could the compiler know?
  return std::move(foo); //so here the move is needed, right?
}

There the move is needed, I suppose?

lornova
  • 5,917
  • 8
  • 39
  • 68
user2015453
  • 4,234
  • 5
  • 22
  • 25
  • 15
    When you are using Visual Studio. – R. Martinho Fernandes Feb 13 '13 at 14:57
  • 3
    Just FYI in the second case, you *cannot* have any usable references/pointers to foo anymore when you return from the function. – R. Martinho Fernandes Feb 13 '13 at 14:57
  • 2
    What are you doing with the returned value? `Foo f = meh();` worked with (N)RVO already in C++98. – Bo Persson Feb 13 '13 at 15:00
  • 1
    I wonder if explicitly calling std::move will prevent NRVO... – thang Feb 13 '13 at 15:09
  • @thang it depends: it prevents NRVO if the constructor has side-effects; it does not prevent NRVO otherwise (as-if rule). Whether the compiler will still do NRVO is a different question, and needs an answer to "which compiler?" first. – R. Martinho Fernandes Feb 13 '13 at 15:11
  • but it's more than just the constructor? it would be a little presumptive of the compiler to assume the meaning of std::move, I think, so that it can just completely elide away the entire line std::move(foo)? – thang Feb 13 '13 at 15:13
  • @thang: `std::move` is a function template, so the compiler doesn't have to assume the meaning. It can inspect the definition. As it happens, though, compilers can also assume the meanings of standard functions if they want to. – Steve Jessop Feb 13 '13 at 15:18
  • 1
    `std::move` is an identity operation. It never actually does anything. It's just a marker for rvalues. If the compiler has the move constructor of `Foo` on hand it can see if it has observable effects and decide upon that. If it has no observable effects, how can you tell the difference? – R. Martinho Fernandes Feb 13 '13 at 15:18
  • I can't believe it's not a duplicate. If it really isn't, then +1 for posting a reference question for future duplicates (which I'm sure will arise). – Christian Rau Feb 13 '13 at 15:28
  • 1
    "We may have references/pointers to foo" - if you do, you're not allowed to use them after the `return` statement, regardless of `std::move`! – user253751 Mar 26 '16 at 07:35
  • `Foo()` is a prvalue, not an xvalue – M.M Feb 27 '17 at 22:51

6 Answers6

138

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.

Steve Jessop
  • 257,525
  • 32
  • 431
  • 672
  • 55
    C++: bone simple. Clearly. – Lightness Races in Orbit Nov 11 '13 at 18:24
  • 48
    C++: if you don't laugh, you'll cry. – mackenir Aug 04 '15 at 14:14
  • 2
    What about trying to return variables declared as `std::unique_ptr` when function is declared to return `std::unique_ptr`? In gcc and mingw-w64 it just works, but vanilla mingw (based gcc 4.9.3, targeting `i686-pc-cygwin`) needs `std::move(x)` to compile. – rr- Oct 12 '15 at 07:01
  • @rr: not sure. I'd expect to need the `std::move` in that case (because the types don't match and so copy elision is off the table), but I could be overlooking something. The fact it differs between 32-bit and 64-bit mingw is odd. I can't off-hand think of a reason why the compiler or platform authors would intend that. – Steve Jessop Oct 12 '15 at 09:33
  • Thanks, that makes sense. Just to make things straight, mingw-w64 is not just a 64-bit version of mingw - it's a fork that introduces some substantial updates. – rr- Oct 12 '15 at 09:48
  • What if one wants to retain move elision but forbid copy-construction without deleting (or making private) the copy constructor of the class? – Kentzo Nov 06 '18 at 19:03
38

The move is unnecessary in both cases. In the second case, std::move is superfluous because you are returning a local variable by value, and the compiler will understand that since you're not going to use that local variable anymore, it can be moved from rather than being copied.

Andy Prowl
  • 114,596
  • 21
  • 355
  • 432
23

On a return value, if the return expression refers directly to the name of a local lvalue (i.e. at this point an xvalue) there is no need for the std::move. On the other hand, if the return expression is not the identifier, it will not be moved automatically, so for example, you would need the explicit std::move in this case:

T foo(bool which) {
   T a = ..., b = ...;
   return std::move(which? a : b);
   // alternatively: return which? std::move(a), std::move(b);
}

When returning a named local variable or a temporary expression directly, you should avoid the explicit std::move. The compiler must (and will in the future) move automatically in those cases, and adding std::move might affect other optimizations.

David Rodríguez - dribeas
  • 192,922
  • 20
  • 275
  • 473
  • 1
    It should be noted, if one of the params of the ternary is created in-place, it will be constructed into the return variable directly. However a move on it will prevent this. This makes the second alternative more flexible - one will only move the named arg. – user362515 Oct 13 '16 at 12:40
19

There are lots of answers about when it shouldn't be moved, but the question is "when should it be moved?"

Here is a contrived example of when it should be used:

std::vector<int> append(std::vector<int>&& v, int x) {
  v.push_back(x);
  return std::move(v);
}

ie, when you have a function that takes an rvalue reference, modifies it, and then returns a copy of it. Now, in practice, this design is almost always better:

std::vector<int> append(std::vector<int> v, int x) {
  v.push_back(x);
  return v;
}

which also allows you to take non-rvalue parameters.

Basically, if you have an rvalue reference within a function that you want to return by moving, you have to call std::move. If you have a local variable (be it a parameter or not), returning it implicitly moves (and this implicit move can be elided away, while an explicit move cannot). If you have a function or operation that takes local variables, and returns a reference to said local variable, you have to std::move to get move to occur (as an example, the trinary ?: operator).

Yakk - Adam Nevraumont
  • 235,777
  • 25
  • 285
  • 465
2

A C++ compiler is free to use std::move(foo):

  • if it is known that foo is at the end of its lifetime, and
  • the implicit use of std::move won't have any effect on the semantics of the C++ code other than the semantic effects allowed by the C++ specification.

It depends on the optimization capabilities of the C++ compiler whether it is able to compute which transformations from f(foo); foo.~Foo(); to f(std::move(foo)); foo.~Foo(); are profitable in terms of performance or in terms of memory consumption, while adhering to the C++ specification rules.


Conceptually speaking, year-2017 C++ compilers, such as GCC 6.3.0, are able to optimize this code:

Foo meh() {
    Foo foo(args);
    foo.method(xyz);
    bar();
    return foo;
}

into this code:

void meh(Foo *retval) {
   new (retval) Foo(arg);
   retval->method(xyz);
   bar();
}

which avoids calling the copy-constructor and the destructor of Foo.


Year-2017 C++ compilers, such as GCC 6.3.0, are unable to optimize these codes:

Foo meh_value() {
    Foo foo(args);
    Foo retval(foo);
    return retval;
}

Foo meh_pointer() {
    Foo *foo = get_foo();
    Foo retval(*foo);
    delete foo;
    return retval;
}

into these codes:

Foo meh_value() {
    Foo foo(args);
    Foo retval(std::move(foo));
    return retval;
}

Foo meh_pointer() {
    Foo *foo = get_foo();
    Foo retval(std::move(*foo));
    delete foo;
    return retval;
}

which means that a year-2017 programmer needs to specify such optimizations explicitly.

atomsymbol
  • 286
  • 7
  • 10
  • 4
    @M.M I don't care about C++ terminology in this instance that much. The expression "call to std::move" in the answer has meaning equivalent to "use of std::move". – atomsymbol Feb 27 '17 at 22:59
  • @M.M Do you have further suggestion on how to improve the answer? – atomsymbol Feb 27 '17 at 23:03
  • @atomsymbol this is a great informational answer, that adds to the existing ones, I don't know what the fuss is about. – Avin Kavish Jun 18 '20 at 04:01
-8

std::move is totally unnecessary when returning from a function, and really gets into the realm of you -- the programmer -- trying to babysit things that you should leave to the compiler.

What happens when you std::move something out of a function that isn't a variable local to that function? You can say that you'll never write code like that, but what happens if you write code that's just fine, and then refactor it and absent-mindedly don't change the std::move. You'll have fun tracking that bug down.

The compiler, on the other hand, is mostly incapable of making these kinds of mistakes.

Also: Important to note that returning a local variable from a function does not necessarily create an rvalue or use move semantics.

See here.

  • 6
    The note in the final paragraph is wrong. The compiler is required to treat it as an rvalue. – R. Martinho Fernandes Feb 13 '13 at 15:09
  • 2
    @R.MartinhoFernandes Yes and no, it will be treated as an rvalue but my compiler prefers elision to move construction and hence it's kind of true to say that there'll be no move semantics (because the move constructor won't be called at all) – Benj Feb 13 '13 at 15:11
  • 3
    The first paragraph is wrong. It is almost always true that returning `std::move` is a bad idea, but there exist cases where returning `std::move` is the right thing to do. – Yakk - Adam Nevraumont Feb 13 '13 at 15:44