when to use move in function calls

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

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.

Tags:

C++

Move