Subject: Re: [boost] [variant] Please vote for behavior
From: Neil Groves (neil_at_[hidden])
Date: 2013-01-27 19:32:37
> I think this comes down to my poor definition of use. I was thinking of
> > "use" as apply public methods and mentally classifying assignment,
> > destruction as operations preparing objects for "use". I am somewhat
> > confused about the move-from standardisation and will attempt to study
> > in more detail. My perception of this is that the object is in a rather
> > state and if I can't call public accessors and therefore their
> > relationships no longer hold I think it could be argued that the
> > class-invariants have been broken.
> You could define it that way, but then you lose any power to use the
> invariant to reason about program correctness. Again, for class
> invariants to have any power, they have to never break except during
> individual mutations.
This is my the root of my concern. If I understand correctly we now have a
new "state" of an object which isn't related to any of it's attributes
during its lifetime. And this becomes an implicit part of preconditions to
functions. However unlike preconditions in languages like Eiffel, I cannot
test to see to determine if the precondition is valid. I have tried to read
around the C++ specification but I'm finding it hard to comprehend.
> The way you deal with this situation is to say that those "public
> accessors" (or whatever) have preconditions that include "not in the
> moved-from state."
> >> > It is interestingly divergent from the typical Design by Contract
> >> > idiom. It is much more usual, in my experience for the
> >> > class-invariants to hold up to and including entry to the destructor
> > I don't understand what any of that means.
> > I'm sorry for not being clearer. From my understanding of DbC the
> > class-invariants must hold upon entry and exit of public functions with
> > exceptions that the class-invariants may not hold upon entry into the
> > constructors
> > and upon exit of the destructor.
Yeah, well the object doesn't exist in those periods, so I just add
> "during the object's lifetime" and the definition is complete.
I agree that this is a good definition and appears consistent with other
languages with which I am familiar. I was under the misapprehension that
the object being moved was being put into a new state which did not satisfy
the class-invariants. I go this impression from reading earlier entries in
this thread. This is clearly not the case as you have clarified below.
I now think I understand that an object that has been moved has its state
altered such that the precondition of all functions may now be invalid
except for the assignment operator and the destructor. Is this correct? I'm
really sorry for being so slow to comprehend this. I seem to be being
> > Since I can no longer operate upon the object after it has been moved
> Who says you can no longer operate upon the object after it has been
> it seems we have a new "undefined state" that doesn't gel well with
> > typical DbC application.
> No, it's just a state. The moved-from state for std::vector is the same
> as its empty state. You can do all the same things with it.
Ah ha! Thank you this was my central misunderstanding from reading a mix of
> > As I stated previously I don't label this as bad. I label it as
> > interesting.
> It's not as interesting as you're making it out to be, or at least it
> doesn't have to be. Creating "interesting" exceptions just makes it
> harder to reason about code. There's a coherent point of view where the
> usual definition of "invariant" holds, that fully describes the
> situation. Why not adopt that point of view instead of changing the
> meaning of "invariant?"
I now understand. I certainly did not want to have an unjustified update to
my mental model without justification. I'm happy that the move support does
not require any such adjustment.
> Incidentally, this is the same reason the basic exception-safety
> guarantee doesn't say "the object needs to be destructible, but its
> invariants don't necessarily hold." There's no need to say that
> provided you correctly describe the invariant.
Exactly, since the class-invariant holds upon entry of the destructor. The
world appears consistent once more :-)
> >> > If I understand correctly the standard is divergent
> >> divergent from what?
> > My experience of DbC with Eiffel, D. I was simply referring to the odd
> > state of the object where it is still 'live' yet cannot have public
> > functions called despite the caller meeting pre-conditions.
> The state of the object in question is one of the preconditions. Can I
> write v.front()? Not if the v is an empty container. There's no
Yes, I see that now that the move simply alters state that affects
pre-conditions. I assume the pre-condition is always query-able for all of
the affect standard library types; otherwise that would be a subtle
difference. The standard has a requirement for implementers of move
constructors that the post-condition of the move operation leaves the
moved-from object in a state whereby the precondition for assignment and
the destructor are true. It is actually as simple as that.
> > This seems to require an adjustment to the way one reasons about code
> > albeit in a not particularly troublesome way.
> Aù contraire, I would find that "adjustment" extremely troubling, which
> is why I don't go there.
The requirement for an adjustment was worrying me and my motivation for
embarrassing myself by disclosing my confusion!
> >> > to allow sensible compiler implementations of clean-up code for
> >> > moved-from objects.
> >> I don't understand what you're saying here, either.
> > I think I'm probably wasting your time. I should probably attempt to
> > improve my understanding of the currently specified move semantics.
> IMO you should attempt to adopt a way of explaining those semantics to
> yourself that doesn't require tearing asunder fundamental definitions,
> like "invariant," upon which we rely to understand code. :-)
> I think while I do not understand the Rationale for allowing
> > assignment and destruction of moved-from objects
> Destruction is trivial to explain:
> void f(bool q, X a)
> std::vector<X> v;
> if (q)
> // <=== What happens to a right here?
> Remember that q and the set of things potentially moved-from can be
> arbitrarily complex, so although the above example could be handled
> easily in a compiler's codegen, in general it can't.
I imagine the main problem is that we might need to traverse function
implementations in other translation units that may not be available to
complete the analysis.
> The reasons for assignment are in part practical. The complexity of
> vector::insert explodes when you support move semantics but can't assign
> to moved-from elements. You need at least two implementations in that
Thanks for the guidance. I struggled to find an example that explained why
assignment was treated specially. This really helped. I really didn't grok
the non-destructive part of the non-destructive move.
> > I am not in a position to make intelligent comments. I try to make
> > that stop me from posting; sometimes I suffer the delusion that I
> > actually understand things!
> I think you're understanding move semantics, but you understand them in
> a way that is harmful to your understanding of other things. I suggest
> you pick an understanding that's consistent with the other theories. ;-)
Actually no, my misunderstand was definitely with the move semantics. My
grounding in DbC is solid, having been involved with several popular
> Dave Abrahams
> BoostPro Computing Software Development Training
> http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost
Thank you for taking the considerable time to help clarify my
understanding. It has been extremely useful to me. I couldn't get this
clear no matter how hard I tried from reading material alone.
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk