Boost logo

Boost :

Subject: Re: [boost] [variant] Please vote for behavior
From: Dave Abrahams (dave_at_[hidden])
Date: 2013-01-30 11:09:04


on Mon Jan 28 2013, Neil Groves <neil-AT-grovescomputing.com> wrote:

>> I think this comes down to my poor definition of use. I was thinking of
>
>> > "use" as apply public methods and mentally classifying assignment,
>> copying,
>> > destruction as operations preparing objects for "use". I am somewhat
>> > confused about the move-from standardisation and will attempt to study
>> this
>> > in more detail. My perception of this is that the object is in a rather
>> new
>> > 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

Not necessarily. As I mentioned, the moved-from state of your
std::vector is the same as its default-constructed state. It's not
"new."

> which isn't related to any of it's attributes during its lifetime.

I have no idea what that means. The "state" of an object is *defined*
by the values of its attributes. So of course, in the moved-from state
its attributes will have particular values and relationships.

> And this becomes an implicit part of preconditions to
> functions.

Nothing needs to be implicit.

> However unlike preconditions in languages like Eiffel, I cannot test
> to see to determine if the precondition is valid.

This has nothing to do with the particulars of Eiffel or C++, and anyway
it is the nature of preconditions that (in general) you can't test them
for validity. For example,

     // PRECONDITION: s points to a null-terminated byte string
     std::size_t strlen(char const* s)

> I have tried to read around the C++ specification but I'm finding it
> hard to comprehend.

What does "read around" mean? I agree that the standard isn't easy to
comprehend, especially not all at once, but it sounds like you mean
something else.

>> 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 the 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.

You can adopt that worldview, but as I mentioned in my previous email,
it's a disempowering one.

> I go this impression from reading earlier entries in this thread. This
> is clearly not the case as you have clarified below.

There's no absolute truth here, because it depends on your definition of
"invariant." It's just a matter of whether you want the word
"invariant" to have any power. You could say "an invariant is never
violated except during mutation or any of these other conditions...,"
but that weakens the notion to the point of uselessness.

> 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?

In practice, that is true, by virtue of what the standard says it will
do with such objects. You are free to write your own generic function
that operates on type T, and may, for example, swap moved-from objects.
Of course, then you need to make "moved-from Ts are swappable" a
documented precondition.

>> 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
>> difference.
>>
>
> Yes, I see that now that the move simply alters state that affects
> pre-conditions. I assume the pre-condition is always query-able

I have no idea what that means. As noted above, in general,
pre-conditions can't be tested.

>> 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)
>> v.push_back(std::move(a));
>> // <=== 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.

No, in general you may require an arbitrary amount of storage to keep
track at runtime of which objects can or cannot be destroyed.

>> > 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.

You call it a "mis-understanding;" I call it a "very disempowering
understanding." Potato potahto.

> My grounding in DbC is solid, having been involved with several
> popular implementations.

I never implied that there was anything wrong with your understanding of
DbC.

> 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.

Glad to help :-)

-- 
Dave Abrahams
BoostPro Computing                  Software Development        Training
http://www.boostpro.com             Clang/LLVM/EDG Compilers  C++  Boost

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