Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why does C++ move semantics leave the source constructed?

In C++11, "move semantics" was introduced, implemented via the two special members: move constructor and move assignment. Both of these operations leave the moved-from object constructed.

Wouldn't it have been better to leave the source in a destructed state? Isn't the only thing you can do with a moved-from object is destruct it anyway?

like image 804
Howard Hinnant Avatar asked Oct 31 '15 18:10

Howard Hinnant


People also ask

Does C have move semantics?

C doesn't have a direct equivalent to move semantics, but the problems that move semantics solve in c++ are much less common in c: As c also doesn't have copy constructors / assignment operators, copies are by default shallow, whereas in c++ common practice is to implement them as deep copy operations or prevent them ...

Why do we need move semantics?

Move semantics allows you to avoid unnecessary copies when working with temporary objects that are about to evaporate, and whose resources can safely be taken from that temporary object and used by another.

What does the move constructor do?

A move constructor enables the resources owned by an rvalue object to be moved into an lvalue without copying.

What does move operator do in C++?

In the C++ programming language, the move assignment operator = is used for transferring a temporary object to an existing object. The move assignment operator, like most C++ operators, can be overloaded. Like the copy assignment operator it is a special member function.


1 Answers

In the "universe of move operations" there are four possibilities:

target          source   is            is left ---------------------------------------------------------- constructed <-- constructed  // C++11 -- move construction constructed <-- destructed assigned    <-- constructed  // C++11 -- move assignment assigned    <-- destructed 

Each of these operations is useful! std::vector<T>::insert alone could make use of the first three. Though note:

  • The source of the 2nd and 4th should not have automatic, static or thread storage duration. The storage duration of the source must be dynamic, else the compiler will call the destructor on the already destructed object. And no, the compiler can not (in general) track if an object has been moved-from:

X x1, x2; if (sometimes) {     x1 = std::move(x2); } // Is x2 moved-from here? 

  • The 2nd and 4th can be emulated by the 1st and 3rd respectively, by simply manually calling the destructor on the source after the operation.

  • The 1st and 3rd are crucial. Algorithms such as std::swap and std::sort regularly need both of these operations. These algorithms do not need to destruct any of their input objects — only change their values.

Armed with this knowledge, in the 2001-2002 time frame I focused my efforts on the two operations that left their source constructed because these two operations would have the largest (positive) impact on what was then C++98. I knew at the time that if I did not curtail the ambitions of this project, that it would never succeed. Even curtailed, it was borderline too-ambitious to succeed.

This curtailment is acknowledged in the original move semantics proposal under the section titled "Destructive move semantics".

In the end, we simply gave up on this as too much pain for not enough gain. However the current proposal does not prohibit destructive move semantics in the future. It could be done in addition to the non-destructive move semantics outlined in this proposal should someone wish to carry that torch.

For more details on what one can do with a moved-from object, see https://stackoverflow.com/a/7028318/576911

like image 180
Howard Hinnant Avatar answered Sep 22 '22 00:09

Howard Hinnant