Contiguous storage containers and move semantics

380 views Asked by At

How can a container be both contiguous and support move semantics at the same time?

An example with std::vector: When push_back() is called using std::move on an lvalue:

std::vector<MyClass> v;

MyClass obj;
MyClass obj2;

vt.push_back(std::move(obj));
vt.push_back(std::move(obj2));

obj and obj2 are not necessarily allocated next to each other in memory. Now as vector must have its elements in contiguous memory, how would move semantics work in this case? It seems to me that it must copy the obj2 to vector v's contiguous memory region (next to obj), otherwise the contiguosity requirement would not be satisfied. But that requires a copy, not a move. What is the difference between the above and this then?:

MyClass obj;
MyClass obj2;

vt.push_back(std::move(obj));
vt.push_back(obj2);
2

There are 2 answers

0
Cameron On

You just need to read a little bit more about move semantics :-)

Moving an object does not change the address of the object itself. It merely calls the move constructor (or assignment operator, etc. depending on context) of another instance of the object, which is passed the one that is to be moved.

In this example, the vector does create two MyClass objects within its internal storage, one per push_back. But for the first one, instead of calling the copy constructor and passing obj (via MyClass const&), it calls the move constructor and passes the rvalue reference (MyClass&&) to obj. It is then up to that constructor to move the contents of the obj object into the one inside the vector.

In other words, the objects themselves are being created in the vector, and its only their contents that are moved (what it means to 'move' an object is potentially different for each type, hence it's the move constructor's job to do the actual moving).

Note that even with std::move, the move constructor may not be called -- there may not be one, for example, or there may be one that isn't noexcept (in which case std::vector can't use it in all circumstances without violating its exception safety guarantees).

0
user2485710 On

Your std::move is a cast, it's an unconditional cast and it can easily decay in a copy if you are not using it with the proper semantics.

In other words writing std::move doesn't guarantee you anything other than a T&& type which by itself doesn't guarantee you move semantics .