
Mathias Gaunard wrote:
Ion Gaztañaga wrote :
I don't think move should be implemented as swap, because the resource (shared memory) is still there floating around:
Indeed, but that is the only way to really keep the strong invariant of one-way construction (which certainly is useful, since it allows optimal object performance by avoiding tests for "emptiness" and leads to safer code since operations on empty objects may not happen).
Yes but this might be surprising to the user, specially when objects are stored in containers and the user does not "see" the move operation.
Ideally, we would want destructive move semantics (move semantics where the moved-from object is destructed when move is performed) but those are not what C++0x provides, unfortunately.
Destructive move semantics would be nice to have, but I think they are complementary to non-destructive ones and ideally I would like to have compiler support to detect "dangling references" (moved, thus destroyed objects) at compile time.
//other_shm will be in default-constructed state shm = move(other_shm)
Why must that state be the default-constructed one? It could just be an unspecified private state than can only be reached from a move, and where only the minimum required operations, such as destruction, are valid.
Correct. It's a design decision.
However, that means potential overhead for those operations. And I personally still do not know what operations are required to be valid on moved-from objects, since that is badly specified, which means in the worst case you have to support all assignment and copying scenarios from/to moved-from objects.
I think there is no overhead. If a private state can be achieved representing "empty object" you can make it public.
There is overhead, but only less than with allowing two-phase construction, unless the rollback semantics are hard to maintain on assignment due to exception safety issues (you might also choose not to support them here and fall back to the moved state on failure).
I understand your points. And I appreciate the one-construction invariant. But that invariant will be broken with move semantics (because they are non-destructive) and you can have non-operational objects. In practice, I've found default constructors useful for non-copyable elements, because they make some code easier without adding any penalty (because the internal "moved" state must exist) and the default constructor makes your code easier to write when branches are around. Imagine a class that has a movable-only type: class MyClass { private: movable_only mo_; }; If I want to implement two-phase construction (for whatever reason) I need to use optional or use heap allocation (in some situations dynamic memory is not available, in embedded systems or when you want to place the object inside shared memory). I also need to initialize the member in the constructor argument list, which is really annoying if I need to do some non-trivial calculations to obtain the data to construct the member: MyClass () : mo_(/*non-trivial code should go here to initialize mo_*/) { //... }; Of course, I could call an static member that returns some data, but I would need to repeat that for every argument or mo_). With default constructors I can do this: MyClass () : mo_() { //Non trivial code... //... //I have all the data to correctly construct mo_ mo_ = move(movable_only(...)); }; Another option would be to call an static function that returns a movable_only object: MyClass () : mo_(static_create_movable_only_from_data(...)) {}; but this can be more costly than the default constructed case, because I need to forward all the data to the static function. I'm sure you can workaround these problems, but I think they complicate coding. Regards, Ion