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.
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.
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.
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...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With