
on Sat Jan 26 2013, Neil Groves <neil-AT-grovescomputing.com> wrote:
On Fri, Jan 25, 2013 at 11:58 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Fri Jan 25 2013, Neil Groves <neil-AT-grovescomputing.com> wrote:
I think variant after move is like int without initialization: int i; cout << i; // don't do this If the fact, that moved-from objects are only good for destruction or assignment-to is accepted, then invariants for moved-from objects are allowed to be violated.
I completely agree with the notion that a moved-from object simply should not be used in any manner.
Well, that's just wrong, for the non-destructive move model used by the
standard. The standard library relies on the ability to both assign and destroy moved-from elements. If you want destructive move, that's a whole research project. We on the committee who created rvalue references couldn't figure out how to make it work.
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. 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.
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 moved?
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.
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?" 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.
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.
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.
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) 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. 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 case.
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. ;-) -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost