Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Are move semantics incomplete?

Move semantics replace copy semantics in situations where copying is inefficient. Copy semantics deals fully with copyable objects, including const objects.

Already, there exists a myriad of non-copyable objects in c++11, for example std::unique_ptr. These objects rely on move semantics completely because moving from an object allows for invalidating it. This is important (imho) for popular design patterns like RAII.

A problem occurs when a const non-copyable object is assigned to an area of memory. Such an object can't be recovered in any way.

This is obviously important during the lifetime of the object, because of its constness. At the end of it's lifetime, when the destructor is called however, the (non-existent) object is briefly non-const.

I suggest that a moving destructor could be a valuable addition to the move semantics model.

Consider a simple situation where an unique_ptr is used in an unordered_set. You can insert into this set using a move constructor (or construct "emplace"), however if you wanted to move this pointer to another unordered_set (i.e. keeping it const) it would be impossible.

Essential, there is a iterator insert((possibly const) key&&) but no const key&& erase(iterator). In fact it would be impossible. The container could only be extended to return some pointer to the key, and forget about it.

A moving destructor could solve this ie const MyClass&& ~MyClass(), since it would only violate const during destruction (when the compiler considers the object is invalid anyway).

EDIT: I should point outconst MyClass&& ~MyClass() const actually makes more sense. The destructor doesn't have to modify anyhting, only destroy the object as if it were no longer a valid handle to whatever resource it controlled.

like image 231
user3125280 Avatar asked Jan 13 '14 07:01

user3125280


3 Answers

Imho you have identified a real need.

Your solution sounds a lot like what I have called destructive move semantics. This possibility is described in the original move semantics proposal. I think such a design is possible, though it is not without its problems. As far as I know, no one is working this area on the standards committee.

There are simpler ways to extract move-only types out of the associative containers that would not require language changes (aside maybe from allowing type-punning without undefined behavior).

N3645 is a library-only proposal which puts a nested node_ptr type into each container. The node_ptr is a lot like a unique_ptr. It has unique ownership of a node in an associative container. But when you dereference it, you get non-const access to the value_type in the node, instead of the node itself. extract and insert members are added to the associative containers allowing one to insert and remove nodes (owned by the node_ptr) to/from the containers.

You could use this to remove a node from a container, and then move a move-only type out of the node, and let ~node_ptr() clean up the node when you are done with it. The paper includes this example to demonstrate this functionality:

set<move_only_type> s;
s.emplace(...);
move_only_type mot = move(*s.extract(s.begin())); // extract, move, deallocate node

Note that s.extract is noexcept, as is ~node_ptr() of course. If the move construction of move_only_type is noexcept, then this whole operation is noexcept. Otherwise if the move construction throws, the set is left as if the item had been erased from the set.

At the moment, no progress is being made on N3645. It has not been voted into a working draft, and I have no confidence that it ever will be.

Update C++17

I stand corrected: The functionality I describe above was voted into C++17 with P0083R3. Thanks to Cosme for reminding me of this in the comments below.

like image 143
Howard Hinnant Avatar answered Oct 31 '22 14:10

Howard Hinnant


Sorry, but the premise is flawed.

An unordered_set doesn't actually hold const objects. It's just not giving you write access to the contained elements. That's a property of the accessors only.

It would be possible to add an key erase(iterator) function which just moves the element out to a temporary. I'm not sure why you'd want a key&& there.

As for const MyClass&& ~MyClass() const, that doesn't make sense for three reasons: dtors have neither return types nor CV classification, nor is overload resolution done for them.

like image 38
MSalters Avatar answered Oct 31 '22 13:10

MSalters


So basically you're saying it should be possible to move a const object into another const object and destroy the original?

Sorry but I think the whole point of making it const is to prevent this.

Otherwise it would form a loophole: you could destroy-move a const object out of its memory location, then you destroy-move another const object into the memory location of the first one (via placement new).

Now the object has changed even though it was const... so essentially const was useless.

See comments below...

like image 1
user541686 Avatar answered Oct 31 '22 14:10

user541686