
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