
Ion Gaztañaga wrote:
Since current move semantics are non-destructive the state of the object should be usable. For objects that have a default-constructor (like shared_ptr or vector) the moved state could be equal to default-constructed (which is consistent *and* usable).
I want to correct this. Default-constructed "could" be ok, but no optimal. For example: std::vector<T> myvect(...); std::vector<T> myvect2(std::move(myvect)); The move operation could be implemented as "swap" so myvect could hold memory previously hold by myvect. This opens the possibility to reuse existing resources. Take for example the implementation of vector with move semantics: This is the old buffer class vector { // ... T *buf_; size_type length_; size_type capacity_; }; Let's insert an object in the beginning. The vector has enough capacity we just need to move the objects and assign the new one in the first slot. Now if moved objects can be reused (code not compiled and it might have flaws). //Suppose length_ > 0... if(length_ > 0){ allocator.construct(buf_+length_, move(buf_[length-1])); ++length_; //Move values one position //Reuse of stl algorithms with a simple iterator wrapper //that moves objects. Don't worry about the moved //value since it can be reused std::copy_backwards ( move_iterator(buf_) , move_iterator(buf_ + length_ - 2) , move_iterator(buf_ + 1)); buf[0] = value; //or if value was catched as a rvalue reference buf[0] = move(value); } else{ allocator.construct(buf_, move(value)); ++length_; } move_iterator is described here: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1771.html, search "24 - Iterators library" to find it. Simple, efficient and easy. And exception safe (when coded correctly, which might not be my case ;-) ). If moved objects can't be reused, want can I do with buf[0]? Should I start doing placement destruction, and do a placement new after that? What if the second step throws? God! So one important point is that reusing objects leads to easier code in many cases. Transition from a usual std::vector to a move-capable vector is really easy if we have an iterator move-wrapper (whose operator *() returns a rvalue-reference instead of an lvalue one) and if moved objects can be reused. The implementation just needs to wrap the copy operations between objects that were already in the container with move_iterator. The same happens if we have two arrays of strings in our application std::string str_array[SIZE]; //.. strings are filled std::string str_other_strings[SIZE * 2]; std::copy(move_iterator(&str_array[0] ,move_iterator(&str_array[SIZE] ,move_iterator(&str_other_strings[0]); //if move is implemented as swap(), str_array //strings still have memory //other operations... //This can lead to ZERO allocations if previous //strings had memory taken from the move-target for(int i = 0; i < SIZE; ++i){ str_array[0] += "prefix_"; str_array[0] += boost::lexical_cast(i); } So my points are: * reusing moved objects leads to easier code. * reusing moved objects might improve performance. * reusing has no impact with temporaries because they are going to be destroyed anyway. * the user can explicitly request resource liberation for moved objects or destroy them if he wants to be sure that resources were liberated and about bloat (it has no guarantee if the state of the moved objects is something like "destructor will be safely called"). Regards, Ion