Moving: what is needed?

What is needed to use the std :: string move assignment operator (in VC11)?

I was hoping it would be used automatically, since v would not be needed after the assignment. Is std :: move required in this case? If so, I could also use non-C ++ 11 exchange.

#include <string> struct user_t { void set_name(std::string v) { name_ = v; // swap(name_, v); // name_ = std::move(v); } std::string name_; }; int main() { user_t u; u.set_name("Olaf"); return 0; } 
+6
source share
3 answers

I was hoping it would be used automatically, since v would not be needed after the assignment. In this case, std :: move is required?

Motion should always be explicitly specified for lvalues, unless they are returned (by value) from the function.

This prevents accidental movement. Remember: movement is a destructive action; you do not want this to happen simply.

It would also be strange if the semantics of name_ = v; changed depending on whether it was the last line in the function. After all, this is completely legal code:

 name_ = v; v[0] = 5; //Assuming v has at least one character. 

Why does the first line sometimes make a copy and move to another time?

If so, I could also use non-C ++ 11 exchange.

You can do as you like, but std::move more obvious with respect to intent. We know what that means and what you do with it.

+11
source

The accepted answer is a good answer (and I supported it). But I wanted to consider this issue in more detail:

The core of my question is: why doesn’t he choose the destination for moving the operator automatically? The compiler knows that v is not used after the job, right? Or does C ++ 11 not require the compiler to be that smart?

This possibility was considered during the development of the semantics of displacement. As a last resort, you might want the compiler to do some static analysis and, if possible, jump from objects:

 void set_name(std::string v) { name_ = v; // move from v if it can be proven that some_event is false? if (some_event) f(v); } 

Ultimately, requiring such analysis from the compiler is very difficult. Some compilers may do the proof, while others may not. This leads to the fact that the code is not very portable.

So, what about some simpler cases without operators?

 void foo() { X x; Y y(x); X x2 = x; // last use? move? } 

Well, it's hard to see if y.~Y() noticed by x . And generally speaking:

 void foo() { X x; // ... // lots of code here // ... X x2 = x; // last use? move? } 

it is difficult for the compiler to analyze this to find out if x is really no longer used after building the copy before x2 .

So, the original “move” sentence gave a rule for implicit moves that were really simple and very conservative:

l Values ​​can be implicitly transferred from cases where copy permission is already permissible.

For instance:

 #include <cassert> struct X { int i_; X() : i_(1) {} ~X() {i_ = 0;} }; struct Y { X* x_; Y() : x_(0) {} ~Y() {assert(x_ != 0); assert(x_->i_ != 0);} }; X foo(bool some_test) { Y y; X x; if (some_test) { X x2; return x2; } y.x_ = &x; return x; } int main() { X x = foo(false); } 

Here, according to the rules of C ++ 98/03, this program may or may not state, depending on whether elision copying occurs on return x . If this happens, the program works fine. If this does not happen, the program approves.

And so it was motivated: when RVO is allowed, we are already in an area where there are no guarantees regarding the value of x . Thus, we should be able to take advantage of this freedom and move from x . The risk looked small, and the advantage looked huge . This would not only mean that many existing programs would become much faster with a simple recompilation, but it would also mean that now we can return "just move" types from the factory. This is a very big risk advantage.

Later in the standardization process, we got a little greedy and also said that implicit movement occurs when the by-value parameter is returned (and the type corresponds to the return type). The benefits here seem quite large, although the probability of code breaking is slightly larger, since this is not the case when the RVO was (or is) legal. But I have no demonstration of code violation for this case.

So, ultimately, the answer to your main question is that the original design of the semantics of movements took a very conservative route regarding breaking existing code. If this were not so, it would surely have been brought down on the committee. At the end of the process, several changes took place that made the design more aggressive. But by this time the main proposal was firmly established in the standard with the support of the majority (but not unanimous).

+7
source

In your example, set_name takes a string by value. Inside set_name , however, v is an lvalue. Let's look at these cases separately:

 user_t u; std::string str("Olaf"); // Creates string by copying a char const*. u.set_name(std::move(str)); // Moves string. 

Inside set_name you call the assignment operator std::string , which carries an unnecessary copy. But there is also an rvalue operator= overload , which makes sense in your case:

 void set_name(std::string v) { name_ = std::move(v); } 

Thus, the only copy that takes place is string construction ( std::string("Olaf") ).

+5
source

Source: https://habr.com/ru/post/915128/


All Articles