C++ curiosities: move semantics is not about moving

by Max Galkin

C++ move semantics is not about moving, but about keeping as many things as possible where they already are. Yes, at a very high level of abstraction one can say that move semantics is about “moving ownership” of a resource from on object to another. But performance benefits of move semantics do not come from these high levels of abstraction, they come from the very earthly fact that a move operation usually needs to touch fewer bytes in memory than a copy operation. (If you’re new to C++ move semantics you might like to know that std::move isn’t about moving things either.)

Perhaps, a good analogy for explaining what move semantics moves and what it doesn’t move is the process of selling/buying some large property. Let’s say you need a skyscraper. And you know a guy, who sells a skyscraper which fits your needs exactly. It’s an existing building, it has a certain known address, and it’s huge. You want to buy it, but, alas, the legal system in the city (you live in the PlusPlusCity98) is a bit quirky: the ownership belongs to the person who constructed the skyscraper and the ownership is non-transferable. Then the only option you have (since you want exactly the same building, and have a distaste for bribing the city officials):

  1. Buy a piece of land nearby.
  2. Ask the owner of the skyscraper to give you the blueprints of the building, and to give you access inside to see every floor and office in the skyscraper.
  3. Construct a full replica of the existing skyscraper on your land.
  4. Inform the owner when the replica is done.
  5. The owner tears the original skyscraper down to sell the land underneath.

Technically, you could say that the skyscraper was moved: its address has changed, but not its internal structure. But this is in fact analogous to C++ copy semantics, not move semantics. This happens in C++98 when you pass a vector<T> by value into a function. Even if the original vector was a “temporary” value, it can not legally “sell” its data to another vector, that data has to be copied byte by byte to a new location.

Many skyscrapers are actually built this way.

Many skyscrapers are actually built this way.

 

Now, contrast this with the customs of the PlusPlusCity11. This city lets people buy and sell skyscrapers using special documents called “titles”. A title is an official paper that describes the skyscraper and states its precise address. Though owners try to never publish the precise addresses of their skyscrapers, titles are still relatively easy to forge. The system works only because most citizens see forgery as immoral and know that such a forgery can lead to the collapse of the Universe. Now to buy a skyscraper in this city you:

  1. Obtain an empty title paper from the city officials.
  2. Invite the current owner of the skyscraper and copy the contents of his title paper into your title paper. (Citizens are not free to give away the title papers themselves, the city tracks how many title papers every citizen ever obtained and asks to return them eventually — no city is free of bureaucracy!).
  3. Nullify the legal power of the previous owner’s title, for example, by striking out the address part on his paper.
  4. The previous owner can still use his nullified title paper to buy another skyscraper, or he can return the paper to the city officials, so that they can recycle it.

Voilà, the skyscraper is yours now, but not a stone had to be moved! And yet this is how the “move semantics” works in C++11 (and that’s where all its performance benefits come from): you get to keep most of the data in the place where it is already, you only need to copy the information from the “title” itself, and nullify the previous owner’s “title”. In more technical terms, a move operation in C++ consists of two steps:

  1. “Shallow copy” — copy the “source” data structure into a new place, but don’t copy anything that it is pointing to, copy just the addresses.
  2. “Destructor disarmament” — change the “source” data structure so that when its destructor (or any other “destructive” operation, such as an assignment or .clear()) is called, it does not destroy the data it used to own through the pointers. You can, for example, nullify the pointers.

This “shallow copy + disarmament” explanation, I think, is a better mental model for the mechanism of “move semantics” in C++ than an intuitive model of some kind of “data teleportation”. It helps realize that the performance gains come not from the nature of the operation we are performing (“copy” vs “move”), but from the reduced amount of copying — in the case of “move” we are only making a “shallow copy” of the data, as opposed to full copy, and some areas of memory can remain untouched at their current address.

In a future post I will illustrate this with code examples and introduce a more formal way of describing the gains.