12

I am currently learning more about all the c++11/14 features and wondering when to use std::move in function calls.

I know I should not use it when returning local variables, because this breaks return value optimisation, but I do not really understand where in function calls casting to a rvalue actually helps.

maxbachmann
  • 2,862
  • 1
  • 11
  • 35

2 Answers2

16

When a function accepts an rvalue reference, you have to provide an rvalue (either by having already a prvalue, or using std::move to create an xvalue). E.g.

void foo(std::string&& s);

std::string s;

foo(s);            // Compile-time error
foo(std::move(s)); // OK
foo(std::string{}) // OK

When a function accepts a value, you can use std::move to move-construct the function argument instead of copy-constructing. E.g.

void bar(std::string s);

std::string s;

bar(s);             // Copies into `s`
bar(std::move(s));  // Moves into `s`

When a function accepts a forwarding reference, you can use std::move to allows the function to move the object further down the call stack. E.g.

template <typename T>
void pipe(T&& x)
{
    sink(std::forward<T>(x));
}

std::string s;

pipe(s);             // `std::forward` will do nothing
pipe(std::move(s));  // `std::forward` will move
pipe(std::string{}); // `std::forward` will move
Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • 2
    so when I do not need the variable anymore it makes sence to just use std::move? – maxbachmann Apr 26 '19 at 10:36
  • 1
    @maxbachmann: in general, yes. But for things like primitive types, there's no benefit. And if the function you're calling takes a `const&`, there's no benefit as well. – Vittorio Romeo Apr 26 '19 at 10:37
  • why is there no benefit for primitive types? – maxbachmann Apr 26 '19 at 10:43
  • 3
    @maxbachmann Because (a) there is nothing to move [moving is about indirect resources hidden behind pointers, mainly], and (b) there is no mechanism with which to move them [no move ctors!] – Lightness Races in Orbit Apr 26 '19 at 10:44
  • `bar(std::string{});` isn't a move, `s` is initialised from the expression – Caleth Apr 26 '19 at 10:53
  • @Caleth: is that guaranteed even prior to 17? – Vittorio Romeo Apr 26 '19 at 11:18
  • [I think so](https://timsong-cpp.github.io/cppwp/n3337/dcl.init.list#1) `std::string{}` is a *direct-list-initialization* for `s`. It's certainly a prvalue – Caleth Apr 26 '19 at 11:37
  • @Caleth: That was a direct initialisation from another `std::string` though. So, to perform the initialisation, the move ctor was invoked. It's not like you're passing a `"string literal"` or something else that triggers a different `std::string` ctor – Lightness Races in Orbit Apr 26 '19 at 12:34
9

When you have some substantial object, and you're passing it as an argument to a function (e.g. an API, or a container emplace operation), and you will no longer need it at the callsite, so you want to transfer ownership, rather than copying then "immediately" losing the original. That's when you move it.

void StoreThing(std::vector<int> v);

int main()
{
    std::vector<int> v{1,2,3,4,5,6/*,.....*/};
    StoreThing(v);
}

// Copies `v` then lets it go out of scope. Pointless!

versus:

void StoreThing(std::vector<int> v);

int main()
{
    std::vector<int> v{1,2,3,4,5,6/*,.....*/};
    StoreThing(std::move(v));
}

// Better! We didn't need `v` in `main` any more...

This happens automatically when returning local variables, if RVO hasn't been applied (and note that such an "optimisation" is mandated since C++17 so you're right to say that adding a "redundant" std::move in that case can actually be harmful).

Also it's pointless to std::move if you're passing something really small (particularly a non-class thing which cannot possibly have a move constructor, let alone a meaningful one!) or you know you're passing into a function that accepts its arguments const-ly; in that case it's up to you as to whether you want to save the added source code distraction of a std::move that won't do anything: on the surface it's wise, but in a template you may not be so sure.

Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • i dont understand "you want to transfer ownership rather than copying then "immediately" losing the original" is it "you want to transfer ownership rather than ( copying then "immediately" losing the original)" ? isnt moving for immediately losing the original? – 463035818_is_not_an_ai Apr 26 '19 at 10:14
  • @user463035818 Correct - this is indicated by the word tenses (and shown in the examples) but I'll clarify it with a comma. – Lightness Races in Orbit Apr 26 '19 at 10:24
  • yep, now I get what you mean with "immediately", together with the example it completely makes sense – 463035818_is_not_an_ai Apr 26 '19 at 11:23