
On Oct 17, 2009, at 12:26 PM, Thomas Klimpel wrote:
Howard Hinnant wrote:
So why do it? What's the benefit even if the cost is small?
Consider std::remove: This is one of the few algorithms in <algorithm> which is not a "resource preserving" permutation. Consider remove({a, b, c, d, e}, c). This will result in the sequence {a, b, d, e, e'}. To achieve this result, the following move assignments will take place:
c = move(d); d = move(e);
The first move assignment is not into a resource-less c. If move assignment into c doesn't "clear", when do those resources go away? For most resources (such as memory), it doesn't really matter when. The resources will still be owned if move-assignment doesn't aggressively destroy them (probably by e').
But for some resources (threads, file handles, mutexes), it will matter if they live too long. The client calling std::remove may or may not destruct e' "soon". But the client asked std::remove to remove c (and presumably all of c's resources). I do not think it would be a good idea for std::remove to also say: Oh, and you better destruct e' too just to make sure c is really gone.
I was initially looking for an example of this sort to "prove" that "clear" is required, but came to realize that I probably won't find one. (The examples Dave gave me are sufficient to "prove" that "clear" is required, but they are related to subtleties of the C++ language and not inherent in moving itself.)
But why do I think that this example doesn't "prove" that "clear" is required? Consider "remove({a, b, c, d, c}, c)" instead of "remove ({a, b, c, d, e}, c)". This will result in the sequence {a, b, d, d', c}. So the assumption that the client asked std::remove to remove c (and presumably all of c's resources) is simply not true. The client really has to destruct the elements of the tail range to ensure that c is really gone, even if the move-assignment operator calls "clear".
That's a good counter example. I should've prefaced my example with something like: The client has already unique'd his sequence and knows that the desired removed element is not at the end of the sequence. I also made another mistake in my last post: unique is not a "resource preserving" permutation.
This is the benefit of move assignment "clearing" potentially significant resources aggressively. I believe this benefit outweighs the cost.
My current understanding is that there are more subtle reasons why move assignment should "clear" potentially significant resources aggressively.
There is still the open point whether self-move-assignment should be forbidden. I wrote in another mail: "On the other hand, as long as the absence of conclusive counter examples can't be "proved", ruling that move-assignment may generally be implemented as swap will be risky or worse." I think the same should apply to self-move- assignment: As long as it can't be proved that forbidding self-move- assignment does no harm, ruling that self-move-assignment is forbidden will be risky or worse.
When move assigning into a resource-less lhs, self move assignment appears to me to be harmless. Note that I do not advocate that move-assignment be generally implemented as swap. I generally advise that move assignment be implemented as: 1. Destroy non-memory resources. 2. Move assign base classes. 3. Move assign data members. 4. Fix up invariants. When 1 and 4 are no-ops, it should be safe to let the compiler default this for you. -Howard