Boost logo

Boost :

Subject: Re: [boost] [contract] move operations and class invariants
From: Gavin Lambert (gavinl_at_[hidden])
Date: 2017-11-29 22:45:16


On 30/11/2017 03:15, Peter Dimov wrote:
> Lorenzo Caminiti wrote:
>
>> C++ requires that moved-from objects can still be destructed. Because
>> contract programming requires class invariants to hold at destructor
>> entry, it follows that moved-from objects must still satisfy class
>> invariants.
>
> No, moved-from objects must be valid objects. Not only can they be
> destroyed, they must be usable. Their state is unspecified, but
> invariants hold.

In general you should expect to be able to call any method which is
valid on a default-constructed object, *especially* assignment operators
(as it's relatively common to reassign a moved-from object). (You
cannot, however, actually assume that it will return the same answers as
a default-constructed object would.)

It *might* also be legal to call other methods with more restrictive
preconditions, but that's sort of the point -- you can't assume this
either way, so actually doing so isn't really valid.

The part about moved-from objects being in an unspecified state is not
meant to imply that they could be "zombie" objects or otherwise invalid
(because that shouldn't happen), it just means that it's allowed for the
class to implement a move as a "true" move (give away storage, so now
the instance is empty) or as a copy (both objects now have the same data
in separate storages), or somewhere in between (such as a swap).

To put it another way, if you move-assign a vector to another vector, it
is perfectly legal for the source vector to not be empty afterwards.
(For example, instead of a destroy-and-swap it could be implemented as a
pure swap. Or even as a copy, although that's less likely with modern
STLs.)

This is also why this code is perfectly valid:

     std::vector<int> a { 1, 2, 3 };
     std::vector<int> b;
     b = std::move(a);
     a.clear();
     a.push_back(4);
     // a == { 4 }, b == { 1, 2, 3 }

But omitting the call to clear() would be a bug -- it's still legal, but
there's no guarantee what the contents of "a" would be at the end if you
didn't explicitly clear it, so it is probably unintended.

(At this point language lawyers might jump on me that vector does
actually provide somewhat more specific guarantees about how move-assign
behaves. But the point stands for generic types.)


Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk