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);
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 perpush_back
. But for the first one, instead of calling the copy constructor and passingobj
(viaMyClass const&
), it calls the move constructor and passes the rvalue reference (MyClass&&
) toobj
. It is then up to that constructor to move the contents of theobj
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'tnoexcept
(in which casestd::vector
can't use it in all circumstances without violating its exception safety guarantees).