Let me preface by saying that I have read some of the many questions already asked regarding move semantics. This question is not about how to use move semantics, it is asking w
Your example gives it away: your code is not exception-safe, and it makes use of the free-store (twice), which can be nontrivial. To use pointers, in many/most situations you have to allocate stuff on the free store, which is much slower than automatic storage, and does not allow for RAII.
They also let you more efficiently represent non-copyable resources, like sockets.
Move semantics aren't strictly necessary, as you can see that C++ has existed for 40 years a while without them. They are simply a better way to represent certain concepts, and an optimization.
Can't the client already take advantage of pointers to do everything move semantics gives us? If so, then what is the purpose of move semantics?
Your second example gives one very good reason why move semantics is a good thing:
std::string *f()
{
std::string *s = new std::string("some long string");
return s;
}
int main()
{
// still super-fast pointer swap!
std::string *a = f();
delete a;
return 0;
}
Here, the client has to examine the implementation to figure out who is responsible for deleting the pointer. With move semantics, this ownership issue won't even come up.
If an exception might be thrown before
delete a
is called, that's still not a real problem just make a guard or useunique_ptr
.
Again, the ugly ownership issue shows up if you don't use move semantics. By the way, how
would you implement unique_ptr
without move semantics?
I know about auto_ptr
and there are good reasons why it is now deprecated.
is it really worth all the trouble with the double ampersands?
True, it takes some time to get used to it. After you are familiar and comfortable with it, you will be wondering how you could live without move semantics.
Your string example is great. The short string optimization means that short std::string
s do not exist in the free store: instead they exist in automatic storage.
The new
/delete
version means that you force every std::string
into the free store. The move
version only puts large strings into the free store, and small strings stay (and are possibly copied) in automatic storage.
On top of that your pointer version lacks exception safety, as it has non-RAII resource handles. Even if you do not use exceptions, naked pointer resource owners basically forces single exit point control flow to manage cleanup. On top of that, use of naked pointer ownership leads to resource leaks and dangling pointers.
So the naked pointer version is worse in piles of ways.
move
semantics means you can treat complex objects as normal values. You move
when you do not want duplicate state, and copy
otherwise. Nearly normal types that cannot be copied can expose move
only (unique_ptr
), others can optimize for it (shared_ptr
). Data stored in containers, like std::vector
, can now include abnormal types because it is move
aware. The std::vector
of std::vector
goes from ridiculously inefficient and hard to use to easy and fast at the stroke of a standard version.
Pointers place the resource management overhead into the clients, while good C++11 classes handle that problem for you. move
semantics makes this both easier to maintain, and far less error prone.
I thoroughly enjoyed all the answers and comments! And I agree with all of them. I just wanted to stick in one more motivation that no one has yet mentioned. This comes from N1377:
Move semantics is mostly about performance optimization: the ability to move an expensive object from one address in memory to another, while pilfering resources of the source in order to construct the target with minimum expense.
Move semantics already exists in the current language and library to a certain extent:
- copy constructor elision in some contexts
- auto_ptr "copy"
- list::splice
- swap on containers
All of these operations involve transferring resources from one object (location) to another (at least conceptually). What is lacking is uniform syntax and semantics to enable generic code to move arbitrary objects (just as generic code today can copy arbitrary objects). There are several places in the standard library that would greatly benefit from the ability to move objects instead of copy them (to be discussed in depth below).
I.e. in generic code such as vector::erase
, one needs a single unified syntax to move values to plug the hole left by the erased valued. One can't use swap
because that would be too expensive when the value_type
is int
. And one can't use copy assignment as that would be too expensive when value_type
is A
(the OP's A
). Well, one could use copy assignment, after all we did in C++98/03, but it is ridiculously expensive.
shouldn't all non-primitive type members be pointers to speed up movement
This would be horribly expensive when the member type is complex<double>
. Might as well color it Java.