Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

when to use move in function calls [duplicate]

Tags:

c++

move

I am currently learning mor 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.

like image 504
maxbachmann Avatar asked Apr 26 '19 10:04

maxbachmann


People also ask

When should you use std :: move?

std::move is used to indicate that an object t may be "moved from", i.e. allowing the efficient transfer of resources from t to another object.

Should I return std :: move?

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.

Is std :: move faster?

Std::move is not faster than straight up copying. So as the title says; i have an implementation where i copy big objects and thought std::move would give a huge performance boost over straight up copying, but it didn't (was roughly the same time).

How does STD move work?

In C++11, std::move is a standard library function that casts (using static_cast) its argument into an r-value reference, so that move semantics can be invoked. Thus, we can use std::move to cast an l-value into a type that will prefer being moved over being copied. std::move is defined in the utility header.


2 Answers

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
like image 97
Vittorio Romeo Avatar answered Oct 19 '22 23:10

Vittorio Romeo


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.

like image 43
Lightness Races in Orbit Avatar answered Oct 19 '22 23:10

Lightness Races in Orbit