[variant] Please vote for behavior (Was: Basic rvalue and C++11 features seupport)

Current implementation of recursive_wrapper move constructor is not optimal: recursive_wrapper<T>::recursive_wrapper(recursive_wrapper&& operand) : p_(new T(std::move(operand.get()) )) { } During descussion were made following proposals: I: Leave it as is - bad performance - can not provide noexcept guarantee II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations + good performance + provides noexcept guarantee for move constructor + optimization will be used even if varinat has no type with trivial default constructor + easy to implement - triggers an assert when user tries to reuse moved object - adds an empty state to the recursive_wrapper III: Make recursive_wrapper and variant cooperate, enable move for varinat in the presence of recursive_wrappers only when there is at least one type that is nothrow-default-constructible, regardless of whether it's current. It is easyer to understand by example: typedef variant<int, recursive_wrapper<foo>> V; V v1( std::move(v2) ); This move-constructs v1 from v2 and leaves int() into v2. + good performance + provides noexcept guarantee for rcursive_wrapper + does not adds an empty state - optimization won't trigger if varinat has no type with trivial default constructor - hard to implement - user may be obscured by the fact, that v2 from the example now contains int IV: After move away, delay construction of type held by recursive_wrapper till it will be be required. +/- good performance? but more checks + provides noexcept guarantee for rcursive_wrapper + does not adds an explicit empty state + optimization will be used even if varinat has no type with trivial default constructor +/- not hard to implement --- additional requirement for type T of recursive_wrapper: it must be default constructible - less obvious behavior for user (for example get() function would construct values, allocate memory and can possibly throw) Please, vote for solution or propose a better one. P.S.: As I know Joel de Guzman votes for solution II (and totally agains solution I, because it is too slow). I wanted to leave as is (solution I) or use solution IV, but Joel talked me to solution II. P.P.S.: More info can be obtained from ticket #7718 https://svn.boost.org/trac/boost/ticket/7718 and previous series of letters. -- Best regards, Antony Polukhin

On Mon, Jan 21, 2013 at 5:39 PM, Antony Polukhin <antoshkka@gmail.com> wrote: [snip]
II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations + good performance + provides noexcept guarantee for move constructor + optimization will be used even if varinat has no type with trivial default constructor + easy to implement - triggers an assert when user tries to reuse moved object - adds an empty state to the recursive_wrapper
I'd prefer this option. IMHO, one of the major points of move semantics is optimization and moved-from objects are as good as dead anyway; I wouldn't even try to revive a moved-from object with assignment. But this is a breaking change (is it?), so it should have a prominent note in release notes.

On 1/21/13 10:29 PM, Andrey Semashev wrote:
On Mon, Jan 21, 2013 at 5:39 PM, Antony Polukhin <antoshkka@gmail.com> wrote:
[snip]
II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations + good performance + provides noexcept guarantee for move constructor + optimization will be used even if varinat has no type with trivial default constructor + easy to implement - triggers an assert when user tries to reuse moved object - adds an empty state to the recursive_wrapper
I'd prefer this option. IMHO, one of the major points of move semantics is optimization and moved-from objects are as good as dead anyway; I wouldn't even try to revive a moved-from object with assignment. But this is a breaking change (is it?), so it should have a prominent note in release notes.
No it's not (a breaking change). We don't have move prior to this patch. The only issue is the one Paul Smith notes regarding conservative move and that is only relevant in the extremely rare case when someone tries to do something to a moved-from object. This notion of a "valid" state is not clear to begin with anyway and many say it depends on the library to define what "valid" means. The case of NaN is a very clear example of a perfectly "valid" state similar to what we have here. To require that an object not have such preconditions on operations after move will have a negative impact on optimizations such as this. Me? You know my vote. But Peter's suggestion (III) is also an acceptable route. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Mon, Jan 21, 2013 at 6:12 PM, Joel de Guzman <djowel@gmail.com> wrote:
This notion of a "valid" state is not clear to begin with anyway and many say it depends on the library to define what "valid" means. The case of NaN is a very clear example of a perfectly "valid" state similar to what we have here.
I'm not sure what the NaN example supposed to convey? I think Nevin brought it up to show that std::less<double> is not a weak partial order. And guess what, it technically isn't! Try to put NaNs inside an associative container and watch the world collapse. The big difference is that no operation on non-NaNs that the container performs ever produces a NaN out of thin air. -- Paul Smith

On Monday 21 January 2013 19:06:32 Paul Smith wrote:
On Mon, Jan 21, 2013 at 6:12 PM, Joel de Guzman <djowel@gmail.com> wrote:
This notion of a "valid" state is not clear to begin with anyway and many say it depends on the library to define what "valid" means. The case of NaN is a very clear example of a perfectly "valid" state similar to what we have here.
I'm not sure what the NaN example supposed to convey? I think Nevin brought it up to show that std::less<double> is not a weak partial order. And guess what, it technically isn't! Try to put NaNs inside an associative container and watch the world collapse. The big difference is that no operation on non-NaNs that the container performs ever produces a NaN out of thin air.
Why would a container try to order moved-from elements?

on Mon Jan 21 2013, Andrey Semashev <andrey.semashev-AT-gmail.com> wrote:
On Monday 21 January 2013 19:06:32 Paul Smith wrote:
On Mon, Jan 21, 2013 at 6:12 PM, Joel de Guzman <djowel@gmail.com> wrote:
This notion of a "valid" state is not clear to begin with anyway and many say it depends on the library to define what "valid" means. The case of NaN is a very clear example of a perfectly "valid" state similar to what we have here.
I'm not sure what the NaN example supposed to convey? I think Nevin brought it up to show that std::less<double> is not a weak partial order. And guess what, it technically isn't! Try to put NaNs inside an associative container and watch the world collapse. The big difference is that no operation on non-NaNs that the container performs ever produces a NaN out of thin air.
Why would a container try to order moved-from elements?
Moved-from elements can be left there due to exceptions propagating out of the code. That's a *new* state that wasn't allowed before move-enabling, so if these moved-from elements have a state outside the former invariants, it will break some code. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On Fri, Jan 25, 2013 at 2:32 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Mon Jan 21 2013, Andrey Semashev <andrey.semashev-AT-gmail.com> wrote:
On Mon, Jan 21, 2013 at 6:12 PM, Joel de Guzman <djowel@gmail.com> wrote:
This notion of a "valid" state is not clear to begin with anyway and many say it depends on the
On Monday 21 January 2013 19:06:32 Paul Smith wrote: library
to define what "valid" means. The case of NaN is a very clear example of a perfectly "valid" state similar to what we have here.
I'm not sure what the NaN example supposed to convey? I think Nevin brought it up to show that std::less<double> is not a weak partial order. And guess what, it technically isn't! Try to put NaNs inside an associative container and watch the world collapse. The big difference is that no operation on non-NaNs that the container performs ever produces a NaN out of thin air.
Why would a container try to order moved-from elements?
Moved-from elements can be left there due to exceptions propagating out of the code. That's a *new* state that wasn't allowed before move-enabling, so if these moved-from elements have a state outside the former invariants, it will break some code.
Isn't leaving the class with it's invariants broken simply a defect? From my perspective the class invariants are defined such that the elements are stored and order is maintained. This is such a core class-invariant for the containers under discussion that it seems absurd to consider breaking it. It isn't merely a matter of compatibility, it is a matter of design coherence. The NaN example is explainable by using Design by Contract too. The pre-conditions of the associative container operations are such that the key must not be NaN. Hence the class invariants are maintained and the world is full of unicorns and rainbows. However once NaNs are input, the class invariants are not maintained. This is entirely reasonable.
-- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost
My vote is that we shouldn't even consider allowing the class invariants for ordered containers to be broken. I cannot imagine a motivation for allowing them to be broken. I suspect this may be a symptom of my failure to comprehend part of this discussion. Neil Groves

on Fri Jan 25 2013, Neil Groves <neil-AT-grovescomputing.com> wrote:
On Fri, Jan 25, 2013 at 2:32 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Mon Jan 21 2013, Andrey Semashev <andrey.semashev-AT-gmail.com> wrote:
On Mon, Jan 21, 2013 at 6:12 PM, Joel de Guzman <djowel@gmail.com> wrote:
This notion of a "valid" state is not clear to begin with anyway and many say it depends on the
On Monday 21 January 2013 19:06:32 Paul Smith wrote: library
to define what "valid" means. The case of NaN is a very clear example of a perfectly "valid" state similar to what we have here.
I'm not sure what the NaN example supposed to convey? I think Nevin brought it up to show that std::less<double> is not a weak partial order. And guess what, it technically isn't! Try to put NaNs inside an associative container and watch the world collapse. The big difference is that no operation on non-NaNs that the container performs ever produces a NaN out of thin air.
Why would a container try to order moved-from elements?
Moved-from elements can be left there due to exceptions propagating out of the code. That's a *new* state that wasn't allowed before move-enabling, so if these moved-from elements have a state outside the former invariants, it will break some code.
Isn't leaving the class with it's invariants broken simply a defect?
Yes. IIUC the question here is whether the invariant of variant [;-)] shall be weakened to accommodate efficient move semantics, thereby breaking some code, or not, at some cost (the specific costs to be incurred by various strategies presently under discussion). But I have to admit, I haven't been reading the thread all that carefully, so I could be mis-understanding completely. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On Fri, Jan 25, 2013 at 10:21 AM, Dave Abrahams <dave@boostpro.com> wrote:
Isn't leaving the class with it's invariants broken simply a defect?
Yes. IIUC the question here is whether the invariant of variant [;-)] shall be weakened to accommodate efficient move semantics, thereby breaking some code, or not, at some cost (the specific costs to be incurred by various strategies presently under discussion).
But I have to admit, I haven't been reading the thread all that carefully, so I could be mis-understanding completely.
Instead of weakened, which is one option discussed, I would also characterize much of the discussion as "let's ignore that the invariant is broken, because no one should rely on invariants after move". If you had opinions on that aspect, you might want to weigh in. It would be appreciated. Tony

2013/1/25 Gottlob Frege <gottlobfrege@gmail.com>
On Fri, Jan 25, 2013 at 10:21 AM, Dave Abrahams <dave@boostpro.com> wrote:
Isn't leaving the class with it's invariants broken simply a defect?
Yes. IIUC the question here is whether the invariant of variant [;-)] shall be weakened to accommodate efficient move semantics, thereby breaking some code, or not, at some cost (the specific costs to be incurred by various strategies presently under discussion).
But I have to admit, I haven't been reading the thread all that carefully, so I could be mis-understanding completely.
Instead of weakened, which is one option discussed, I would also characterize much of the discussion as "let's ignore that the invariant is broken, because no one should rely on invariants after move".
If you had opinions on that aspect, you might want to weigh in. It would be appreciated.
Tony
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. Kris

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. 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. It seems that this divergence is of little importance to users of the language since we are not explicitly interested in calling the destructor. If I understand correctly the standard is divergent to allow sensible compiler implementations of clean-up code for moved-from objects. Hence I am in agreement with your sentiment, but wanted to point out that this is unusual use of the DbC rules. Unusual in this case is not meant to imply bad; it is rather interesting.
Kris
Regards, Neil Groves

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.
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.
It seems that this divergence is of little importance to users of the language since we are not explicitly interested in calling the destructor.
Indeed we are! Trying to keep track of which elements have been moved-from so you can avoid calling the destructor gets very hairy, very quickly. Again, we couldn't figure out how to do it.
If I understand correctly the standard is divergent
divergent from what?
to allow sensible compiler implementations of clean-up code for moved-from objects.
I don't understand what you're saying here, either. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

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.
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. Since I 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. As I stated previously I don't label this as bad. I label it as interesting.
It seems that this divergence is of little importance to users of the language since we are not explicitly interested in calling the destructor.
Indeed we are! Trying to keep track of which elements have been moved-from so you can avoid calling the destructor gets very hairy, very quickly. Again, we couldn't figure out how to do it.
It does indeed sound horrendously difficult.
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. This seems to require an adjustment to the way one reasons about code albeit in a not particularly troublesome way.
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. I think while I do not understand the Rationale for allowing assignment and destruction of moved-from objects 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!
-- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost
Regardz ;-) Neil Groves

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

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 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 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. 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 rather dim.
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.
Ah ha! Thank you this was my central misunderstanding from reading a mix of information.
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 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 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) 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.
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.
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 implementations.
-- 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. Thanks, Neil Groves

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

On 1/26/13 7:58 AM, Dave Abrahams wrote:
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.
(The) ability to both assign and destroy? Is that all that it needs? Then we should be OK. A nulled recursive_wrapper can both be assigned and destroyed. It just can't do other things apart from that, such as get the underlying T& and call its member functions. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

on Sat Jan 26 2013, Joel de Guzman <djowel-AT-gmail.com> wrote:
On 1/26/13 7:58 AM, Dave Abrahams wrote:
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.
(The) ability to both assign and destroy? Is that all that it needs?
That's all the standard library will use. However, you still have to be honest about the variant's invariant: it must include the moved-from state.
Then we should be OK. A nulled recursive_wrapper can both be assigned and destroyed. It just can't do other things apart from that, such as get the underlying T& and call its member functions.
I am not familiar enough with the details of recursive_wrapper and how much of it is exposed to users to know whether any of this matters. You have to choose: either the addition of this moved-from state must not invalidate any previous guarantees upon which users may rely, OR you have to tell them that you're breaking backward-compatibility and document that the moved-from state is an "anti-precondition" for all the "other things apart from that." Or you could invent a whole new concept of "invariant" that is allowed to be violated... but I really discourage that! :-) -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On 1/28/13 12:18 AM, Dave Abrahams wrote:
on Sat Jan 26 2013, Joel de Guzman <djowel-AT-gmail.com> wrote:
On 1/26/13 7:58 AM, Dave Abrahams wrote:
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.
(The) ability to both assign and destroy? Is that all that it needs?
That's all the standard library will use. However, you still have to be honest about the variant's invariant: it must include the moved-from state.
Then we should be OK. A nulled recursive_wrapper can both be assigned and destroyed. It just can't do other things apart from that, such as get the underlying T& and call its member functions.
I am not familiar enough with the details of recursive_wrapper and how much of it is exposed to users to know whether any of this matters. You have to choose: either the addition of this moved-from state must not invalidate any previous guarantees upon which users may rely, OR you have to tell them that you're breaking backward-compatibility and document that the moved-from state is an "anti-precondition" for all the "other things apart from that."
Or you could invent a whole new concept of "invariant" that is allowed to be violated... but I really discourage that! :-)
I think we are OK! Come to think of it, the situation is a lot like a "singular" iterator: "Iterators can also have singular values that are not associated with any sequence. [snip] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation." Indeed, for a singular valued iterator, i, you can assign to i and destruct i, place i in a container, etc. You just cannot dereference i, access its underlying value through ->, compare i with another iterator, etc. The same is true with a nulled recursive_wrapper. Being honest about this is just a matter of documentation. I do not see any problem at all with having a "singular" recursive_wrapper value. (BTW, who is the current maintainer of variant? I don't see Itay and Eric here in this list anymore. I've invested heavily on variant and I am willing to be a maintainer of variant or be a co-maintainer with Antony Polukhin) Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Mon, Jan 28, 2013 at 2:02 AM, Joel de Guzman <djowel@gmail.com> wrote:
I think we are OK!
Come to think of it, the situation is a lot like a "singular" iterator:
"Iterators can also have singular values that are not associated with any sequence. [snip] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation."
Indeed, for a singular valued iterator, i, you can assign to i and destruct i, place i in a container, etc. You just cannot dereference i, access its underlying value through ->, compare i with another iterator, etc.
The same is true with a nulled recursive_wrapper.
Being honest about this is just a matter of documentation. I do not see any problem at all with having a "singular" recursive_wrapper value.
Then you're still thinking in terms of destructive move. The reason why you don't see a problem with a "singular valued" recursive_wrapper (that's what we call it now? :-) is that you don't think anyone will try to use it. And, c'mon, it's not just a matter of documentation - we're not trying to weasel our way out of this. Adding a new and entirely different state just for the sake of move construction is a polite way of ignoring the possiblity that moved-from objects will be used. It might win you a case in court, but it's completely missing the point. Besides, drawing from iterators to recursive_wrappers is dangerous. They're completely different things. A recursive_wrapper's value *is* its underlying object's value - it's not a pointer. Talking about "singular values" under this concept doesn't make sense. If you want a recursive_wrapper that has an empty state, that's fine. But it's a different concept that deserves a different type. -- Paul Smith

On Mon, Jan 28, 2013 at 6:10 PM, Paul Smith <pl.smith.mail@gmail.com> wrote:
Then you're still thinking in terms of destructive move. The reason why you don't see a problem with a "singular valued" recursive_wrapper (that's what we call it now? :-) is that you don't think anyone will try to use it. And, c'mon, it's not just a matter of documentation - we're not trying to weasel our way out of this. Adding a new and entirely different state just for the sake of move construction is a polite way of ignoring the possiblity that moved-from objects will be used. It might win you a case in court, but it's completely missing the point.
I'm still missing a use case when a moved-from variant will be used in a previously-working code. It has been stated that variant did not support move so it was copied where it could have been moved before the change. The implicit moving of temporaries seem to pose no problems because temporaries are not used after move. The change will affect cases of explicit move of (non-temporary) variants but it seems unlikely if the code attempts to use the source object after move. Someone suggested that problems can arise with associative containers, but I fail to see how that can happen, keeping in mind that the key is always constant and cannot be moved. So unless I missed something, we're arguing about cases like this: variant< ... > v; foo(move(v)); cout << v; Am I correct?

On Mon, Jan 28, 2013 at 4:34 PM, Andrey Semashev <andrey.semashev@gmail.com> wrote:
I'm still missing a use case when a moved-from variant will be used in a previously-working code.
Did you read the earlier discussion? We've already been through this, I think. http://boost.2283326.n4.nabble.com/variant-Basic-rvalue-and-C-11-features-su... -- Paul Smith

On 1/28/13 10:10 PM, Paul Smith wrote:
On Mon, Jan 28, 2013 at 2:02 AM, Joel de Guzman <djowel@gmail.com> wrote:
I think we are OK!
Come to think of it, the situation is a lot like a "singular" iterator:
"Iterators can also have singular values that are not associated with any sequence. [snip] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation."
Indeed, for a singular valued iterator, i, you can assign to i and destruct i, place i in a container, etc. You just cannot dereference i, access its underlying value through ->, compare i with another iterator, etc.
The same is true with a nulled recursive_wrapper.
Being honest about this is just a matter of documentation. I do not see any problem at all with having a "singular" recursive_wrapper value.
Then you're still thinking in terms of destructive move. The reason why you don't see a problem with a "singular valued" recursive_wrapper (that's what we call it now? :-) is that you don't think anyone will try to use it. And, c'mon, it's not just a matter of documentation - we're not trying to weasel our way out of this. Adding a new and entirely different state just for the sake of move construction is a polite way of ignoring the possiblity that moved-from objects will be used. It might win you a case in court, but it's completely missing the point.
Besides, drawing from iterators to recursive_wrappers is dangerous. They're completely different things. A recursive_wrapper's value *is* its underlying object's value - it's not a pointer. Talking about "singular values" under this concept doesn't make sense.
If you want a recursive_wrapper that has an empty state, that's fine. But it's a different concept that deserves a different type.
Oh man, here we go again. No I am not convinced. Sorry. The standard only mentions assign and destroy as necessary, and Dave confirms that, as did people like Stanley Lippman (C++ Primer 5th Edition). I am also not convinced that drawing from objects with "singular" values is wrong, regardless if it's a pointer or not. I think it is you who's missing the point because Iterators are likewise *not* pointers. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Mon, Jan 28, 2013 at 6:11 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/28/13 10:10 PM, Paul Smith wrote:
On Mon, Jan 28, 2013 at 2:02 AM, Joel de Guzman <djowel@gmail.com> wrote:
I think we are OK!
Come to think of it, the situation is a lot like a "singular" iterator:
"Iterators can also have singular values that are not associated with any sequence. [snip] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation."
Indeed, for a singular valued iterator, i, you can assign to i and destruct i, place i in a container, etc. You just cannot dereference i, access its underlying value through ->, compare i with another iterator, etc.
The same is true with a nulled recursive_wrapper.
Being honest about this is just a matter of documentation. I do not see any problem at all with having a "singular" recursive_wrapper value.
Then you're still thinking in terms of destructive move. The reason why you don't see a problem with a "singular valued" recursive_wrapper (that's what we call it now? :-) is that you don't think anyone will try to use it. And, c'mon, it's not just a matter of documentation - we're not trying to weasel our way out of this. Adding a new and entirely different state just for the sake of move construction is a polite way of ignoring the possiblity that moved-from objects will be used. It might win you a case in court, but it's completely missing the point.
Besides, drawing from iterators to recursive_wrappers is dangerous. They're completely different things. A recursive_wrapper's value *is* its underlying object's value - it's not a pointer. Talking about "singular values" under this concept doesn't make sense.
If you want a recursive_wrapper that has an empty state, that's fine. But it's a different concept that deserves a different type.
Oh man, here we go again.
Sorry, I didn't mean to be irritating. I'm honestly not trying to spite. If this discussion gets heated, I quit.
No I am not convinced. Sorry. The standard only mentions assign and destroy as necessary
Really, where? [moveconstructible] has much stricter requirements than that. This issue has been discussed more than once before, and nothing I say here is my own opinion, so please don't take it out on me. For example, see N3264 (CH-18 and US-85: Clarifying the state of moved-from objects (Revision 1)): http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3264.htm
and Dave confirms that,
I don't want to read into what Dave said too much, because he's here and he can clarify it. But I believe what he said is that specific algorithms, in their own localized context, practically only require destructibility and assignability. And even then, it's not something the standard actually guarantees in general, though, and the requirements are still much stricter, perhaps superfluously so. That's frustrating, no doubt about it, and it's a good selling point for having destructive move semantics - not for breaking the rules.
as did people like Stanley Lippman (C++ Primer 5th Edition).
I didn't read the book, but as you see, there's a lot of controversy and misconception around this subject, so I'll take everything with a grain of salt. If you have a quote, that would be helpful.
I am also not convinced that drawing from objects with "singular" values is wrong, regardless if it's a pointer or not. I think it is you who's missing the point because Iterators are likewise *not* pointers.
Okay, so replace "recursive_wrappers are not pointers" with "recursive_wrappers are not iterators". How does it make it any different? A singular value still doesn't fit in. -- Paul Smith

On 1/29/13 1:40 AM, Paul Smith wrote:
I am also
not convinced that drawing from objects with "singular" values is wrong, regardless if it's a pointer or not. I think it is you who's missing the point because Iterators are likewise*not* pointers.
Okay, so replace "recursive_wrappers are not pointers" with "recursive_wrappers are not iterators". How does it make it any different? A singular value still doesn't fit in.
The point is that iterators can have singular values. If iterators can have them, why can't any other object (have them)? IMO, ultimately, it's a matter of design. You may not agree with a recursive_wrapper being in a "singular" state after move, but that's just your preference. IMO, it's necessary for proxy-like objects that own and hold their subjects by pointer. It's not quite elegant, sure, but C++ is never elegant in many respects for the sake of high performance. I'd trade this quirk for the sake of efficiency any day. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Mon, Jan 28, 2013 at 8:27 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/29/13 1:40 AM, Paul Smith wrote:
I am also
not convinced that drawing from objects with "singular" values is wrong, regardless if it's a pointer or not. I think it is you who's missing the point because Iterators are likewise*not* pointers.
Okay, so replace "recursive_wrappers are not pointers" with "recursive_wrappers are not iterators". How does it make it any different? A singular value still doesn't fit in.
The point is that iterators can have singular values. If iterators can have them, why can't any other object (have them)?
Why should any other object have them? Iterators are a generalization of pointers and that's where they inherit their singular state from. This has absolutely nothing to do with move-semantics, and that's exactly why I say that such inferences, just like the NaN example, are dangerously superficial. A moved-from iterator isn't singular just like a default-constructed recursive_wrapper isn't. Different concepts - different issues. The point I'm trying to make is that conceptually recursive_wrappers don't have a value of their own. They have exactly the same set of states as their underlying type. If this type has a singular value, then and only then does a recursive wrapper around this type has a singular value.
IMO, ultimately, it's a matter of design. You may not agree with a recursive_wrapper being in a "singular" state after move, but that's just your preference.
Okay, I'm not sure what exactly we are disagreeing about anymore. Do we agree that the move semantics we have in C++ are non-destructive (I'm not asking whether you like it or not. At this point it's a fact - not a preference)? If you agree with that, then you should appreciate that it's like that for a reason, whether or not you agree with that reason. A moved-from object should remain in a valid state. You're suggesting meeting this requirement by introducing a new (and yes, breaking) state, let's bluntly call it the "invalid" state, and you don't see what I'm talking about when I say that this is just missing the point? Then there's really nothing more I can say...
IMO, it's necessary for proxy-like objects that own and hold their subjects by pointer. It's not quite elegant, sure, but C++ is never elegant in many respects for the sake of high performance. I'd trade this quirk for the sake of efficiency any day.
That's a rant about how move semantics in C++ turned out (completely intentionally) to be. This discussion isn't about that, it's about how recursive_wrapper should behave under these semantics. -- Paul Smith

On Tue, Jan 29, 2013 at 7:12 AM, Paul Smith <pl.smith.mail@gmail.com> wrote:
On Mon, Jan 28, 2013 at 8:27 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/29/13 1:40 AM, Paul Smith wrote:
I am also
not convinced that drawing from objects with "singular" values is
wrong,
regardless if it's a pointer or not. I think it is you who's missing the point because Iterators are likewise*not* pointers.
Okay, so replace "recursive_wrappers are not pointers" with "recursive_wrappers are not iterators". How does it make it any different? A singular value still doesn't fit in.
The point is that iterators can have singular values. If iterators can have them, why can't any other object (have them)?
Why should any other object have them? Iterators are a generalization of pointers and that's where they inherit their singular state from. This has absolutely nothing to do with move-semantics, and that's exactly why I say that such inferences, just like the NaN example, are dangerously superficial. A moved-from iterator isn't singular just like a default-constructed recursive_wrapper isn't. Different concepts - different issues. The point I'm trying to make is that conceptually recursive_wrappers don't have a value of their own. They have exactly the same set of states as their underlying type. If this type has a singular value, then and only then does a recursive wrapper around this type has a singular value.
IMO, ultimately, it's a matter of design. You may not agree with a recursive_wrapper being in a "singular" state after move, but that's just your preference.
Okay, I'm not sure what exactly we are disagreeing about anymore.
Do we agree that the move semantics we have in C++ are non-destructive (I'm not asking whether you like it or not. At this point it's a fact - not a preference)? If you agree with that, then you should appreciate that it's like that for a reason, whether or not you agree with that reason.
A moved-from object should remain in a valid state. You're suggesting meeting this requirement by introducing a new (and yes, breaking) state, let's bluntly call it the "invalid" state, and you don't see what I'm talking about when I say that this is just missing the point? Then there's really nothing more I can say...
IMO, it's necessary for proxy-like objects that own and hold their subjects by pointer. It's not quite elegant, sure, but C++ is never elegant in many respects for the sake of high performance. I'd trade this quirk for the sake of efficiency any day.
That's a rant about how move semantics in C++ turned out (completely intentionally) to be. This discussion isn't about that, it's about how recursive_wrapper should behave under these semantics.
This discussion might be facilitated if Joel et al (sorry Joel, I don't mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant. Maybe I've inferred each of your respective positions incorrectly, though. - Jeff

On Tuesday 29 January 2013 09:25:11 Jeffrey Lee Hellrung, Jr. wrote:
This discussion might be facilitated if Joel et al (sorry Joel, I don't mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant".
That was my position from the start of this discussion.

On Tue, Jan 29, 2013 at 7:25 PM, Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung@gmail.com> wrote:
On Tue, Jan 29, 2013 at 7:12 AM, Paul Smith <pl.smith.mail@gmail.com> wrote:
On Mon, Jan 28, 2013 at 8:27 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/29/13 1:40 AM, Paul Smith wrote:
I am also
not convinced that drawing from objects with "singular" values is
wrong,
regardless if it's a pointer or not. I think it is you who's missing the point because Iterators are likewise*not* pointers.
Okay, so replace "recursive_wrappers are not pointers" with "recursive_wrappers are not iterators". How does it make it any different? A singular value still doesn't fit in.
The point is that iterators can have singular values. If iterators can have them, why can't any other object (have them)?
Why should any other object have them? Iterators are a generalization of pointers and that's where they inherit their singular state from. This has absolutely nothing to do with move-semantics, and that's exactly why I say that such inferences, just like the NaN example, are dangerously superficial. A moved-from iterator isn't singular just like a default-constructed recursive_wrapper isn't. Different concepts - different issues. The point I'm trying to make is that conceptually recursive_wrappers don't have a value of their own. They have exactly the same set of states as their underlying type. If this type has a singular value, then and only then does a recursive wrapper around this type has a singular value.
IMO, ultimately, it's a matter of design. You may not agree with a recursive_wrapper being in a "singular" state after move, but that's just your preference.
Okay, I'm not sure what exactly we are disagreeing about anymore.
Do we agree that the move semantics we have in C++ are non-destructive (I'm not asking whether you like it or not. At this point it's a fact - not a preference)? If you agree with that, then you should appreciate that it's like that for a reason, whether or not you agree with that reason.
A moved-from object should remain in a valid state. You're suggesting meeting this requirement by introducing a new (and yes, breaking) state, let's bluntly call it the "invalid" state, and you don't see what I'm talking about when I say that this is just missing the point? Then there's really nothing more I can say...
IMO, it's necessary for proxy-like objects that own and hold their subjects by pointer. It's not quite elegant, sure, but C++ is never elegant in many respects for the sake of high performance. I'd trade this quirk for the sake of efficiency any day.
That's a rant about how move semantics in C++ turned out (completely intentionally) to be. This discussion isn't about that, it's about how recursive_wrapper should behave under these semantics.
This discussion might be facilitated if Joel et al (sorry Joel, I don't mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant.
I think this is about a little more than that, even though to be honest, I'm not entirely sure what's the dispute is about too. I think it's closer to what Edward Diener said: The main issue seems to be simply this: are guarantees ( invariants ) for an object of a class meant to cover moved from objects of that class ? All of the choices which you have specified, which popularly boils down to II or III, involves this question. The choice of II answers No to the question above while the choice of III answers Yes to the question above. As absurd as it may sound right now, I think Joel's and mine opinions are much closer than it seems. I like performance too, believe it or not. And it's not just about performance, under non-destructive move semantics there's an entire class of objects which is neither copyable nor movable, and it becomes harder to give no-throw guarantees. I definitely *do* want to be able to move recursive_wrapper by nulling it's pointer. I think what we disagree on is how to get there :-) -- Paul Smith

On 1/30/13 5:00 AM, Paul Smith wrote:
On Tue, Jan 29, 2013 at 7:25 PM, Jeffrey Lee Hellrung, Jr.
This discussion might be facilitated if Joel et al (sorry Joel, I don't mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant.
I think this is about a little more than that, even though to be honest, I'm not entirely sure what's the dispute is about too. I think it's closer to what Edward Diener said:
The main issue seems to be simply this: are guarantees ( invariants ) for an object of a class meant to cover moved from objects of that class ?
All of the choices which you have specified, which popularly boils down to II or III, involves this question. The choice of II answers No to the question above while the choice of III answers Yes to the question above.
As absurd as it may sound right now, I think Joel's and mine opinions are much closer than it seems. I like performance too, believe it or not. And it's not just about performance, under non-destructive move semantics there's an entire class of objects which is neither copyable nor movable, and it becomes harder to give no-throw guarantees. I definitely *do* want to be able to move recursive_wrapper by nulling it's pointer. I think what we disagree on is how to get there :-)
Ok, let's just agree to disagree then and leave it at that. You say that we can't do that now because we are violating the standard if we do. I say that that understanding is wrong. Dave's note is pretty clear to me:
(The) ability to both assign and destroy? Is that all that it needs?
That's all the standard library will use.
I think I've said enough on this subject matter. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Wed, Jan 30, 2013 at 1:40 AM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/30/13 5:00 AM, Paul Smith wrote:
On Tue, Jan 29, 2013 at 7:25 PM, Jeffrey Lee Hellrung, Jr.
This discussion might be facilitated if Joel et al (sorry Joel, I don't mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant.
I think this is about a little more than that, even though to be honest, I'm not entirely sure what's the dispute is about too. I think it's closer to what Edward Diener said:
The main issue seems to be simply this: are guarantees ( invariants ) for an object of a class meant to cover moved from objects of that class ?
All of the choices which you have specified, which popularly boils down to II or III, involves this question. The choice of II answers No to the question above while the choice of III answers Yes to the question above.
As absurd as it may sound right now, I think Joel's and mine opinions are much closer than it seems. I like performance too, believe it or not. And it's not just about performance, under non-destructive move semantics there's an entire class of objects which is neither copyable nor movable, and it becomes harder to give no-throw guarantees. I definitely *do* want to be able to move recursive_wrapper by nulling it's pointer. I think what we disagree on is how to get there :-)
Ok, let's just agree to disagree then and leave it at that.
Listen, Joel, I'll say this once more - I'm not trying to annoy you. This discussion isn't about you or me, it's about variant, and it also seems to be about move semantics in general. I agree to disagree with you, but I still want to understand the view you represent for my own sake, whoever cares to explain it to me. What the language semantics are designed for, what the standard library guarantees and effectively requires, and what the committee seem to advocate for, are conservative move-semantics - ones that require that a moved-from object maintains its invariants (in the case of the library - its requirements). On the other hand we have destructive move-semantics, for the sake of this discussion let's define them as the ones that merely require the moved-from object to remain destructible and assignable. If this is where I'm wrong - I'd love to be shown otherwise, either by giving me a standard citation or a reference to a WG paper that shows it, or by addressing the ones I gave and explaining to me what did I misinterpret. Currently, recursive_wrapper has the fact that it always holds a value as an invariant, or equivalently in the context of variant, variant has a never-empty guarantee and a variant that contains a recursive_wrapper<T> behaves as if it contained a T. Now we have a problem, because nulling recursive_wrapper's pointer, even though it does the trick for destructive move-semantics, breaks these invariants. This problem is in fact not uqniue to variant or recursive_wrapper specifically. If our universal solution to this problem is going to be to introduce an artifical post-move state and have it as an anti-precondition to anything but assignemnt and destruction, then what's the point in having conservative move-semantics in the first place? Destructive move-semantics is what we end up with in practice. We'd be much better off by saying that moved-from objects can break their usual invariants than by having to weaken those invariants. This is my break-down of the situtation. If anyone disagrees with any of this - I'd love to hear why. -- Paul Smith

on Wed Jan 30 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
What the language semantics are designed for, what the standard library guarantees and effectively requires, and what the committee seem to advocate for, are conservative move-semantics - ones that require that a moved-from object maintains its invariants (in the case of the library - its requirements).
An object always maintains its invariants during its lifetime.
On the other hand we have destructive move-semantics,
In what sense are you claiming that we "have" destructive move-semantics? Nobody has ever implemented or even fully-specified destructive move semantics AFAIK. Destructive move semantics are (at this time) nothing more than a fantasy some people have about what they think would be a better world. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On Thu, Jan 31, 2013 at 3:37 AM, Dave Abrahams <dave@boostpro.com> wrote:
On the other hand we have destructive move-semantics,
In what sense are you claiming that we "have" destructive move-semantics? Nobody has ever implemented or even fully-specified destructive move semantics AFAIK. Destructive move semantics are (at this time) nothing more than a fantasy some people have about what they think would be a better world.
It's a figure of speech. I wasn't saying we actually "have" them - I was defining a term and making a distinction. You omitted the rest of the sentence. -- Paul Smith

on Thu Jan 31 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Thu, Jan 31, 2013 at 3:37 AM, Dave Abrahams <dave@boostpro.com> wrote:
On the other hand we have destructive move-semantics,
In what sense are you claiming that we "have" destructive move-semantics? Nobody has ever implemented or even fully-specified destructive move semantics AFAIK. Destructive move semantics are (at this time) nothing more than a fantasy some people have about what they think would be a better world.
It's a figure of speech. I wasn't saying we actually "have" them - I was defining a term and making a distinction.
Ah, thanks.
You omitted the rest of the sentence.
Yes, because I couldn't understand it without understanding "have." You seem to be saying that you can't understand "have" without the rest of the sentence ;-) -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

on Tue Jan 29 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Tue, Jan 29, 2013 at 7:25 PM, Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung@gmail.com> wrote:
This discussion might be facilitated if Joel et al (sorry Joel, I don't mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant.
I think this is about a little more than that, even though to be honest, I'm not entirely sure what's the dispute is about too. I think it's closer to what Edward Diener said:
The main issue seems to be simply this: are guarantees ( invariants ) for an object of a class meant to cover moved from objects of that class?
I don't think that question should really even be on the table. If "invariant" and "guarantee" are to have any useful meaning, the answer must be "yes." -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On 1/30/2013 8:33 PM, Dave Abrahams wrote:
on Tue Jan 29 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Tue, Jan 29, 2013 at 7:25 PM, Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung@gmail.com> wrote:
This discussion might be facilitated if Joel et al (sorry Joel, I don't mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant.
I think this is about a little more than that, even though to be honest, I'm not entirely sure what's the dispute is about too. I think it's closer to what Edward Diener said:
The main issue seems to be simply this: are guarantees ( invariants ) for an object of a class meant to cover moved from objects of that class?
I don't think that question should really even be on the table. If "invariant" and "guarantee" are to have any useful meaning, the answer must be "yes."
I agree with you but that means to me that for a class to be movable an invariant for that class can never be that the class cannot be empty. It seems to me that the Variant class does have an invariant which says that the class can never be empty. So my conclusion is that a Variant can not be movable.

On Fri, Feb 1, 2013 at 5:51 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 1/30/2013 8:33 PM, Dave Abrahams wrote:
on Tue Jan 29 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Tue, Jan 29, 2013 at 7:25 PM, Jeffrey Lee Hellrung, Jr.
<jeffrey.hellrung@gmail.com> wrote:
This discussion might be facilitated if Joel et al (sorry Joel, I don't
mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant.
I think this is about a little more than that, even though to be honest, I'm not entirely sure what's the dispute is about too. I think it's closer to what Edward Diener said:
The main issue seems to be simply this: are guarantees ( invariants ) for an object of a class meant to cover moved from objects of that class?
I don't think that question should really even be on the table. If "invariant" and "guarantee" are to have any useful meaning, the answer must be "yes."
I agree with you but that means to me that for a class to be movable an invariant for that class can never be that the class cannot be empty. It seems to me that the Variant class does have an invariant which says that the class can never be empty. So my conclusion is that a Variant can not be movable.
Not true. First of all, the primary present issue is implementing move semantics for a variant with recursive_wrapper. Non-recursive variants aren't really a problem. Second, specifically for recursive variants, if one of the underlying types is default constructible (trivially, ideally!), then, again no issues (in theory; such a solution isn't presently implemented). So we basically have this very "narrow" case (recursive variant with no detectable default-constructible types) which is the issue, and even then, we have a solution (it's just, by all accounts, suboptimal, since it has to use new sometimes). - Jeff

On 2/1/2013 9:32 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Fri, Feb 1, 2013 at 5:51 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
On 1/30/2013 8:33 PM, Dave Abrahams wrote:
on Tue Jan 29 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Tue, Jan 29, 2013 at 7:25 PM, Jeffrey Lee Hellrung, Jr.
<jeffrey.hellrung@gmail.com> wrote:
This discussion might be facilitated if Joel et al (sorry Joel, I don't
mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant.
I think this is about a little more than that, even though to be honest, I'm not entirely sure what's the dispute is about too. I think it's closer to what Edward Diener said:
The main issue seems to be simply this: are guarantees ( invariants ) for an object of a class meant to cover moved from objects of that class?
I don't think that question should really even be on the table. If "invariant" and "guarantee" are to have any useful meaning, the answer must be "yes."
I agree with you but that means to me that for a class to be movable an invariant for that class can never be that the class cannot be empty. It seems to me that the Variant class does have an invariant which says that the class can never be empty. So my conclusion is that a Variant can not be movable.
Not true. First of all, the primary present issue is implementing move semantics for a variant with recursive_wrapper. Non-recursive variants aren't really a problem.
Second, specifically for recursive variants, if one of the underlying types is default constructible (trivially, ideally!), then, again no issues (in theory; such a solution isn't presently implemented). So we basically have this very "narrow" case (recursive variant with no detectable default-constructible types) which is the issue, and even then, we have a solution (it's just, by all accounts, suboptimal, since it has to use new sometimes).
If you are going to be moving data from an object, but you must still recreate some of that data after the move so as not to leave the object empty, this does not seem to me to be a very successful move. Perhaps we are in the land of "we say it is a 'move' but actually we are doing 'copying' of some of the data and moving the rest". Fair enough, but the gist of 'moving' as I understand it is that the moved from object is empty after the move even though the object still must be in a usable state. I admit that I do not see in the current standard where a moved from object cannot still contain a copy of some of the data which has been moved from it, but I still would have thought that the concept of a movable object is that it is empty of its data after the move.

On 02.02.2013, at 13:54, Edward Diener wrote:
On 2/1/2013 9:32 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Fri, Feb 1, 2013 at 5:51 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
I agree with you but that means to me that for a class to be movable an invariant for that class can never be that the class cannot be empty. It seems to me that the Variant class does have an invariant which says that the class can never be empty. So my conclusion is that a Variant can not be movable.
Not true. First of all, the primary present issue is implementing move semantics for a variant with recursive_wrapper. Non-recursive variants aren't really a problem.
Second, specifically for recursive variants, if one of the underlying types is default constructible (trivially, ideally!), then, again no issues (in theory; such a solution isn't presently implemented). So we basically have this very "narrow" case (recursive variant with no detectable default-constructible types) which is the issue, and even then, we have a solution (it's just, by all accounts, suboptimal, since it has to use new sometimes).
If you are going to be moving data from an object, but you must still recreate some of that data after the move so as not to leave the object empty, this does not seem to me to be a very successful move.
Anything that gives the target object the state the source object had and is cheaper than a copy is a successful move, really. That is what a move ultimately is: an optimization of copying.
Perhaps we are in the land of "we say it is a 'move' but actually we are doing 'copying' of some of the data and moving the rest". Fair enough, but the gist of 'moving' as I understand it is that the moved from object is empty after the move even though the object still must be in a usable state.
No, the gist of 'moving' is that the target object has the source object' state, and the source object is fair game for anything as long as it is still valid. If it's more efficient to only copy some of the data rather than move it, then by all means do that. For example, look at the compiler-created move constructor for a simple data struct: struct foo { std::string s; int i; }; The move constructor will move the string, but just copy the int (because int makes no difference between copy and move). So the source object, after a move, will still have the same value in the int, but an empty string.
I admit that I do not see in the current standard where a moved from object cannot still contain a copy of some of the data which has been moved from it, but I still would have thought that the concept of a movable object is that it is empty of its data after the move.
Consider what happens when you move an int. What does "empty of its data" even mean for an int (or any other POD)? The requirement on moving in the standard is that the moved-from object has an arbitrary valid state. The move operations should therefore be written such that they move to the cheapest-possible valid state, and do it without throwing if at all possible. For variant, this means that it should work this way: 0) If any contained type is not movable, the variant as a whole is not movable. If any contained type's move constructor is not nothrow, the variant's move constructor and assignment aren't either. 1) If the currently active type is not the recursion, move-construct that type in the target variant. Leave the source as-is otherwise. 2) If the active type is the recursion, then 2.1) if there is some type that is nothrow-default-constructible, give the pointer to the target variant, and default-contruct this type in the source, 2.2) if there is no such type, either be not movable, or be not nothrow-movable. I'm not sure which is the better choice here. But note that types that aren't nothrow-default-constructible are unlikely to be nothrow-move-constructible either, so see point 0. Sebastian

On 2/2/2013 10:57 AM, Sebastian Redl wrote:
On 02.02.2013, at 13:54, Edward Diener wrote:
On 2/1/2013 9:32 PM, Jeffrey Lee Hellrung, Jr. wrote:
On Fri, Feb 1, 2013 at 5:51 PM, Edward Diener <eldiener@tropicsoft.com>wrote:
I agree with you but that means to me that for a class to be movable an invariant for that class can never be that the class cannot be empty. It seems to me that the Variant class does have an invariant which says that the class can never be empty. So my conclusion is that a Variant can not be movable.
Not true. First of all, the primary present issue is implementing move semantics for a variant with recursive_wrapper. Non-recursive variants aren't really a problem.
Second, specifically for recursive variants, if one of the underlying types is default constructible (trivially, ideally!), then, again no issues (in theory; such a solution isn't presently implemented). So we basically have this very "narrow" case (recursive variant with no detectable default-constructible types) which is the issue, and even then, we have a solution (it's just, by all accounts, suboptimal, since it has to use new sometimes).
If you are going to be moving data from an object, but you must still recreate some of that data after the move so as not to leave the object empty, this does not seem to me to be a very successful move.
Anything that gives the target object the state the source object had and is cheaper than a copy is a successful move, really. That is what a move ultimately is: an optimization of copying.
Perhaps we are in the land of "we say it is a 'move' but actually we are doing 'copying' of some of the data and moving the rest". Fair enough, but the gist of 'moving' as I understand it is that the moved from object is empty after the move even though the object still must be in a usable state.
No, the gist of 'moving' is that the target object has the source object' state, and the source object is fair game for anything as long as it is still valid. If it's more efficient to only copy some of the data rather than move it, then by all means do that. For example, look at the compiler-created move constructor for a simple data struct:
struct foo { std::string s; int i; };
The move constructor will move the string, but just copy the int (because int makes no difference between copy and move). So the source object, after a move, will still have the same value in the int, but an empty string.
I admit that I do not see in the current standard where a moved from object cannot still contain a copy of some of the data which has been moved from it, but I still would have thought that the concept of a movable object is that it is empty of its data after the move.
Consider what happens when you move an int. What does "empty of its data" even mean for an int (or any other POD)? The requirement on moving in the standard is that the moved-from object has an arbitrary valid state. The move operations should therefore be written such that they move to the cheapest-possible valid state, and do it without throwing if at all possible.
For variant, this means that it should work this way: 0) If any contained type is not movable, the variant as a whole is not movable. If any contained type's move constructor is not nothrow, the variant's move constructor and assignment aren't either. 1) If the currently active type is not the recursion, move-construct that type in the target variant. Leave the source as-is otherwise. 2) If the active type is the recursion, then 2.1) if there is some type that is nothrow-default-constructible, give the pointer to the target variant, and default-contruct this type in the source, 2.2) if there is no such type, either be not movable, or be not nothrow-movable. I'm not sure which is the better choice here. But note that types that aren't nothrow-default-constructible are unlikely to be nothrow-move-constructible either, so see point 0.
Thanks for the explanation. I realize now that my understanding of 'move' was just wrong.

To sum up the vote: 1) recursive_wrapper move constructor implementation must remain as is 2) new class recursive_ptr can be added, which may have optimized move constructors and move assignments + may be used as a drop in replacement for recursive_wrapper 3) variant shall 'cooperate' with recursive_wrapper for better performance So the final solution is a combination of three proposed solutions. Great thanks for all the thoughts, everyone! That was an exiting discussion! -- Best regards, Antony Polukhin

on Sat Feb 02 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
Anything that gives the target object the state the source object had and is cheaper than a copy is a successful move, really. That is what a move ultimately is: an optimization of copying.
I never liked that characterization. If move were an optimization of copy it would have all the semantics of copy... but it doesn't. In a move, the source object state becomes indeterminate. -- Dave Abrahams

On 06.02.2013, at 22:10, Dave Abrahams wrote:
on Sat Feb 02 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
Anything that gives the target object the state the source object had and is cheaper than a copy is a successful move, really. That is what a move ultimately is: an optimization of copying.
I never liked that characterization. If move were an optimization of copy it would have all the semantics of copy...
I disagree. That would be the case if move were a universally applicable optimization, but it isn't. It's a special case optimization. In intent, it optimizes copying for the case where the resulting state of the source object is irrelevant. In practice, the source object must retain some valid state. This is really similar to how the compiler can only apply some optimizations if it can prove certain properties of the code, e.g. that two pointers don't alias (various redundant load or store eliminations) or that a piece of code is side effect free (loop fusion, common subexpression elimination). Sebastian

on Thu Feb 07 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
On 06.02.2013, at 22:10, Dave Abrahams wrote:
on Sat Feb 02 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
Anything that gives the target object the state the source object had and is cheaper than a copy is a successful move, really. That is what a move ultimately is: an optimization of copying.
I never liked that characterization. If move were an optimization of copy it would have all the semantics of copy...
I disagree. That would be the case if move were a universally applicable optimization, but it isn't. It's a special case optimization. In intent, it optimizes copying for the case where the resulting state of the source object is irrelevant. In practice, the source object must retain some valid state.
This is really similar to how the compiler can only apply some optimizations if it can prove certain properties of the code, e.g. that two pointers don't alias (various redundant load or store eliminations) or that a piece of code is side effect free (loop fusion, common subexpression elimination).
Yes, but those are semantics-preserving transformations. I suppose you can call move an optimization if you presume the irrelevancy of the source object has been proven, but usually that proof is part of the optimization process. -- Dave Abrahams

On 07.02.2013, at 21:25, Dave Abrahams wrote:
on Thu Feb 07 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
On 06.02.2013, at 22:10, Dave Abrahams wrote:
on Sat Feb 02 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
Anything that gives the target object the state the source object had and is cheaper than a copy is a successful move, really. That is what a move ultimately is: an optimization of copying.
I never liked that characterization. If move were an optimization of copy it would have all the semantics of copy...
I disagree. That would be the case if move were a universally applicable optimization, but it isn't. It's a special case optimization. In intent, it optimizes copying for the case where the resulting state of the source object is irrelevant. In practice, the source object must retain some valid state.
This is really similar to how the compiler can only apply some optimizations if it can prove certain properties of the code, e.g. that two pointers don't alias (various redundant load or store eliminations) or that a piece of code is side effect free (loop fusion, common subexpression elimination).
Yes, but those are semantics-preserving transformations. I suppose you can call move an optimization if you presume the irrelevancy of the source object has been proven, but usually that proof is part of the optimization process.
That's why move only implicitly kicks in for rvalues and return of local lvalues, where this has indeed been proven. (Sort of. It's actually not true for local lvalues, but then, copy elision can change visible behavior too.) For lvalues, where the compiler can't (or won't) prove it, user intervention is required in the form of std::move. Think of it like of the restrict qualifier for aliasing. Sebastian

Sebastian Redl wrote:
On 07.02.2013, at 21:25, Dave Abrahams wrote:
on Thu Feb 07 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
On 06.02.2013, at 22:10, Dave Abrahams wrote:
on Sat Feb 02 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
Anything that gives the target object the state the source object had and is cheaper than a copy is a successful move, really. That is what a move ultimately is: an optimization of copying.
I never liked that characterization. If move were an optimization of copy it would have all the semantics of copy...
I disagree. That would be the case if move were a universally applicable optimization, but it isn't. It's a special case optimization. In intent, it optimizes copying for the case where the resulting state of the source object is irrelevant. In practice, the source object must retain some valid state.
This is really similar to how the compiler can only apply some optimizations if it can prove certain properties of the code, e.g. that two pointers don't alias (various redundant load or store eliminations) or that a piece of code is side effect free (loop fusion, common subexpression elimination).
Yes, but those are semantics-preserving transformations. I suppose you can call move an optimization if you presume the irrelevancy of the source object has been proven, but usually that proof is part of the optimization process.
That's why move only implicitly kicks in for rvalues and return of local lvalues, where this has indeed been proven. (Sort of. It's actually not true for local lvalues, but then, copy elision can change visible behavior too.) For lvalues, where the compiler can't (or won't) prove it, user intervention is required in the form of std::move. Think of it like of the restrict qualifier for aliasing.
I don't see how move is an optimization of copy since you can move noncopyable types.

on Thu Feb 07 2013, Michael Marcin <mike.marcin-AT-gmail.com> wrote:
Sebastian Redl wrote:
On 07.02.2013, at 21:25, Dave Abrahams wrote:
on Thu Feb 07 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
On 06.02.2013, at 22:10, Dave Abrahams wrote:
on Sat Feb 02 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
Anything that gives the target object the state the source object had and is cheaper than a copy is a successful move, really. That is what a move ultimately is: an optimization of copying.
I never liked that characterization. If move were an optimization of copy it would have all the semantics of copy...
I disagree. That would be the case if move were a universally applicable optimization, but it isn't. It's a special case optimization. In intent, it optimizes copying for the case where the resulting state of the source object is irrelevant. In practice, the source object must retain some valid state.
This is really similar to how the compiler can only apply some optimizations if it can prove certain properties of the code, e.g. that two pointers don't alias (various redundant load or store eliminations) or that a piece of code is side effect free (loop fusion, common subexpression elimination).
Yes, but those are semantics-preserving transformations. I suppose you can call move an optimization if you presume the irrelevancy of the source object has been proven, but usually that proof is part of the optimization process.
That's why move only implicitly kicks in for rvalues and return of local lvalues, where this has indeed been proven. (Sort of. It's actually not true for local lvalues, but then, copy elision can change visible behavior too.) For lvalues, where the compiler can't (or won't) prove it, user intervention is required in the form of std::move. Think of it like of the restrict qualifier for aliasing.
I don't see how move is an optimization of copy since you can move noncopyable types.
Yeah, that's another thing. -- Dave Abrahams

On Thu, Feb 7, 2013 at 6:57 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Thu Feb 07 2013, Michael Marcin <mike.marcin-AT-gmail.com> wrote:
Sebastian Redl wrote:
On 07.02.2013, at 21:25, Dave Abrahams wrote:
on Thu Feb 07 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
On 06.02.2013, at 22:10, Dave Abrahams wrote:
on Sat Feb 02 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
> Anything that gives the target object the state the source object had > and is cheaper than a copy is a successful move, really. That is what > a move ultimately is: an optimization of copying.
I never liked that characterization. If move were an optimization of copy it would have all the semantics of copy...
I disagree. That would be the case if move were a universally applicable optimization, but it isn't. It's a special case optimization. In intent, it optimizes copying for the case where the resulting state of the source object is irrelevant. In practice, the source object must retain some valid state.
This is really similar to how the compiler can only apply some optimizations if it can prove certain properties of the code, e.g. that two pointers don't alias (various redundant load or store eliminations) or that a piece of code is side effect free (loop fusion, common subexpression elimination).
Yes, but those are semantics-preserving transformations. I suppose you can call move an optimization if you presume the irrelevancy of the source object has been proven, but usually that proof is part of the optimization process.
That's why move only implicitly kicks in for rvalues and return of local lvalues, where this has indeed been proven. (Sort of. It's actually not true for local lvalues, but then, copy elision can change visible behavior too.) For lvalues, where the compiler can't (or won't) prove it, user intervention is required in the form of std::move. Think of it like of the restrict qualifier for aliasing.
I don't see how move is an optimization of copy since you can move noncopyable types.
Yeah, that's another thing.
Is the compiler even allowed to choose between copy and move? My understanding is that you can't tell it "this type can't be copied so if I've told you to copy, see if you can move instead". Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

on Thu Feb 07 2013, Emil Dotchevski <emildotchevski-AT-gmail.com> wrote:
On Thu, Feb 7, 2013 at 6:57 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Thu Feb 07 2013, Michael Marcin <mike.marcin-AT-gmail.com> wrote:
Sebastian Redl wrote:
On 07.02.2013, at 21:25, Dave Abrahams wrote:
on Thu Feb 07 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote:
On 06.02.2013, at 22:10, Dave Abrahams wrote:
> > on Sat Feb 02 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote: > >> Anything that gives the target object the state the source object had >> and is cheaper than a copy is a successful move, really. That is what >> a move ultimately is: an optimization of copying. > > I never liked that characterization. If move were an optimization of > copy it would have all the semantics of copy...
I disagree. That would be the case if move were a universally applicable optimization, but it isn't. It's a special case optimization. In intent, it optimizes copying for the case where the resulting state of the source object is irrelevant. In practice, the source object must retain some valid state.
This is really similar to how the compiler can only apply some optimizations if it can prove certain properties of the code, e.g. that two pointers don't alias (various redundant load or store eliminations) or that a piece of code is side effect free (loop fusion, common subexpression elimination).
Yes, but those are semantics-preserving transformations. I suppose you can call move an optimization if you presume the irrelevancy of the source object has been proven, but usually that proof is part of the optimization process.
That's why move only implicitly kicks in for rvalues and return of local lvalues, where this has indeed been proven. (Sort of. It's actually not true for local lvalues, but then, copy elision can change visible behavior too.) For lvalues, where the compiler can't (or won't) prove it, user intervention is required in the form of std::move. Think of it like of the restrict qualifier for aliasing.
I don't see how move is an optimization of copy since you can move noncopyable types.
Yeah, that's another thing.
Is the compiler even allowed to choose between copy and move?
On the basis of whether a move exists, yes. On the basis of whether a copy exists, no.
My understanding is that you can't tell it "this type can't be copied so if I've told you to copy, see if you can move instead".
I think it wouldn't be too hard to code up a copy_else_move(x) function along the same lines as std::move(x). You'd probably use some trait to detect copiability. -- Dave Abrahams

On Fri, Feb 8, 2013 at 12:09 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Thu Feb 07 2013, Emil Dotchevski <emildotchevski-AT-gmail.com> wrote:
On Thu, Feb 7, 2013 at 6:57 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Thu Feb 07 2013, Michael Marcin <mike.marcin-AT-gmail.com> wrote:
Sebastian Redl wrote:
On 07.02.2013, at 21:25, Dave Abrahams wrote:
on Thu Feb 07 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote: > On 06.02.2013, at 22:10, Dave Abrahams wrote: >> on Sat Feb 02 2013, Sebastian Redl <sebastian.redl-AT-getdesigned.at> wrote: >>> Anything that gives the target object the state the source object had >>> and is cheaper than a copy is a successful move, really. That is what >>> a move ultimately is: an optimization of copying. >> >> I never liked that characterization. If move were an optimization of >> copy it would have all the semantics of copy... > > I disagree. That would be the case if move were a universally > applicable optimization, but it isn't. It's a special case > optimization. In intent, it optimizes copying for the case where the > resulting state of the source object is irrelevant. In practice, the > source object must retain some valid state. > > This is really similar to how the compiler can only apply some > optimizations if it can prove certain properties of the code, > e.g. that two pointers don't alias (various redundant load or store > eliminations) or that a piece of code is side effect free (loop > fusion, common subexpression elimination).
Yes, but those are semantics-preserving transformations. I suppose you can call move an optimization if you presume the irrelevancy of the source object has been proven, but usually that proof is part of the optimization process.
That's why move only implicitly kicks in for rvalues and return of local lvalues, where this has indeed been proven. (Sort of. It's actually not true for local lvalues, but then, copy elision can change visible behavior too.) For lvalues, where the compiler can't (or won't) prove it, user intervention is required in the form of std::move. Think of it like of the restrict qualifier for aliasing.
I don't see how move is an optimization of copy since you can move noncopyable types.
Yeah, that's another thing.
Is the compiler even allowed to choose between copy and move?
On the basis of whether a move exists, yes. On the basis of whether a copy exists, no.
My understanding is that you can't tell it "this type can't be copied so if I've told you to copy, see if you can move instead".
I think it wouldn't be too hard to code up a copy_else_move(x) function along the same lines as std::move(x). You'd probably use some trait to detect copiability.
I know you can write code that tells the compiler to move or to copy, but this doesn't mean that the compiler can move if you told it to copy. So these operations are independent as far as the language is concerned, whereas copy constructor elision is allowed entirely at the discretion of the compiler, because A(A(a)) is the same as A(a). Emil Dotchevski Reverge Studios, Inc. http://www.revergestudios.com/reblog/index.php?n=ReCode

on Fri Feb 01 2013, Edward Diener <eldiener-AT-tropicsoft.com> wrote:
On 1/30/2013 8:33 PM, Dave Abrahams wrote:
on Tue Jan 29 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Tue, Jan 29, 2013 at 7:25 PM, Jeffrey Lee Hellrung, Jr. <jeffrey.hellrung@gmail.com> wrote:
This discussion might be facilitated if Joel et al (sorry Joel, I don't mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant.
I think this is about a little more than that, even though to be honest, I'm not entirely sure what's the dispute is about too. I think it's closer to what Edward Diener said:
The main issue seems to be simply this: are guarantees ( invariants ) for an object of a class meant to cover moved from objects of that class?
I don't think that question should really even be on the table. If "invariant" and "guarantee" are to have any useful meaning, the answer must be "yes."
I agree with you but that means to me that for a class to be movable an invariant for that class can never be that the class cannot be empty.
Not so. Throwing move constructors are allowed.
It seems to me that the Variant class does have an invariant which says that the class can never be empty. So my conclusion is that a Variant can not be movable.
Probably not, unless you change (weaken) the invariant... though I haven't really thought through the implications of a possible throwing move constructor. -- Dave Abrahams

On 1/30/13 1:25 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Tue, Jan 29, 2013 at 7:12 AM, Paul Smith <pl.smith.mail@gmail.com> wrote:
On Mon, Jan 28, 2013 at 8:27 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/29/13 1:40 AM, Paul Smith wrote:
I am also
not convinced that drawing from objects with "singular" values is
wrong,
regardless if it's a pointer or not. I think it is you who's missing the point because Iterators are likewise*not* pointers.
Okay, so replace "recursive_wrappers are not pointers" with "recursive_wrappers are not iterators". How does it make it any different? A singular value still doesn't fit in.
The point is that iterators can have singular values. If iterators can have them, why can't any other object (have them)?
Why should any other object have them? Iterators are a generalization of pointers and that's where they inherit their singular state from. This has absolutely nothing to do with move-semantics, and that's exactly why I say that such inferences, just like the NaN example, are dangerously superficial. A moved-from iterator isn't singular just like a default-constructed recursive_wrapper isn't. Different concepts - different issues. The point I'm trying to make is that conceptually recursive_wrappers don't have a value of their own. They have exactly the same set of states as their underlying type. If this type has a singular value, then and only then does a recursive wrapper around this type has a singular value.
IMO, ultimately, it's a matter of design. You may not agree with a recursive_wrapper being in a "singular" state after move, but that's just your preference.
Okay, I'm not sure what exactly we are disagreeing about anymore.
Do we agree that the move semantics we have in C++ are non-destructive (I'm not asking whether you like it or not. At this point it's a fact - not a preference)? If you agree with that, then you should appreciate that it's like that for a reason, whether or not you agree with that reason.
A moved-from object should remain in a valid state. You're suggesting meeting this requirement by introducing a new (and yes, breaking) state, let's bluntly call it the "invalid" state, and you don't see what I'm talking about when I say that this is just missing the point? Then there's really nothing more I can say...
IMO, it's necessary for proxy-like objects that own and hold their subjects by pointer. It's not quite elegant, sure, but C++ is never elegant in many respects for the sake of high performance. I'd trade this quirk for the sake of efficiency any day.
That's a rant about how move semantics in C++ turned out (completely intentionally) to be. This discussion isn't about that, it's about how recursive_wrapper should behave under these semantics.
This discussion might be facilitated if Joel et al (sorry Joel, I don't mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant.
No, Jeff, that is wrong. We are not violating the semantics to variant. It's not about variant. It's about recursive_wrapper. I think people are confused with this. The variant's never-empty guarantee still holds. A variant holding a singular-valued recursive_wrapper is similar to a variant holding a singular valued iterator. The variant is not empty. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On 01/29/13 17:07, Joel de Guzman wrote:
On 1/30/13 1:25 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Tue, Jan 29, 2013 at 7:12 AM, Paul Smith <pl.smith.mail@gmail.com> wrote:
On Mon, Jan 28, 2013 at 8:27 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/29/13 1:40 AM, Paul Smith wrote:
I am also > not convinced that drawing from objects with "singular" values is
wrong,
> regardless if it's a pointer or not. I think it is you who's missing > the point because Iterators are likewise*not* pointers.
Okay, so replace "recursive_wrappers are not pointers" with "recursive_wrappers are not iterators". How does it make it any different? A singular value still doesn't fit in.
The point is that iterators can have singular values. If iterators can have them, why can't any other object (have them)?
Why should any other object have them? Iterators are a generalization of pointers and that's where they inherit their singular state from. This has absolutely nothing to do with move-semantics, and that's exactly why I say that such inferences, just like the NaN example, are dangerously superficial. A moved-from iterator isn't singular just like a default-constructed recursive_wrapper isn't. Different concepts - different issues. The point I'm trying to make is that conceptually recursive_wrappers don't have a value of their own. They have exactly the same set of states as their underlying type. If this type has a singular value, then and only then does a recursive wrapper around this type has a singular value.
IMO, ultimately, it's a matter of design. You may not agree with a recursive_wrapper being in a "singular" state after move, but that's just your preference.
Okay, I'm not sure what exactly we are disagreeing about anymore.
Do we agree that the move semantics we have in C++ are non-destructive (I'm not asking whether you like it or not. At this point it's a fact - not a preference)? If you agree with that, then you should appreciate that it's like that for a reason, whether or not you agree with that reason.
A moved-from object should remain in a valid state. You're suggesting meeting this requirement by introducing a new (and yes, breaking) state, let's bluntly call it the "invalid" state, and you don't see what I'm talking about when I say that this is just missing the point? Then there's really nothing more I can say...
IMO, it's necessary for proxy-like objects that own and hold their subjects by pointer. It's not quite elegant, sure, but C++ is never elegant in many respects for the sake of high performance. I'd trade this quirk for the sake of efficiency any day.
That's a rant about how move semantics in C++ turned out (completely intentionally) to be. This discussion isn't about that, it's about how recursive_wrapper should behave under these semantics.
This discussion might be facilitated if Joel et al (sorry Joel, I don't mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant.
No, Jeff, that is wrong. We are not violating the semantics to variant. It's not about variant. It's about recursive_wrapper. I think people are confused with this. The variant's never-empty guarantee still holds.
The page: http://www.boost.org/doc/libs/1_52_0/doc/html/variant/design.html#variant.de... says: variant may be viewed precisely as a union of exactly its bounded types but having a singular-valued recursive_wrapper violates this view, because there's no way you can dereference a singular-valued recursive_wrapper to get at one of the bounded types. -regards Larry

On 1/30/13 7:23 AM, Larry Evans wrote:
This discussion might be facilitated if Joel et al (sorry Joel, I don't mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant.
No, Jeff, that is wrong. We are not violating the semantics to variant. It's not about variant. It's about recursive_wrapper. I think people are confused with this. The variant's never-empty guarantee still holds.
The page:
http://www.boost.org/doc/libs/1_52_0/doc/html/variant/design.html#variant.de...
says:
variant may be viewed precisely as a union of exactly its bounded types
but having a singular-valued recursive_wrapper violates this view, because there's no way you can dereference a singular-valued recursive_wrapper to get at one of the bounded types.
That is actually a good point. I didn't think of it that way, but yeah, I can appreciate that. So, I think I am inclined to agree with Jeff now. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

on Wed Jan 30 2013, Joel de Guzman <djowel-AT-gmail.com> wrote:
On 1/30/13 1:25 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Tue, Jan 29, 2013 at 7:12 AM, Paul Smith <pl.smith.mail@gmail.com> wrote:
This discussion might be facilitated if Joel et al (sorry Joel, I don't mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant.
No, Jeff, that is wrong. We are not violating the semantics to variant. It's not about variant. It's about recursive_wrapper. I think people are confused with this. The variant's never-empty guarantee still holds. A variant holding a singular-valued recursive_wrapper is similar to a variant holding a singular valued iterator. The variant is not empty.
But Joel, IIUC a variant never actually, from the abstract interface point-of-view, contains a recursive_wrapper anyway. Isn't the recursive_wrapper just a detail required to make it possible for a variant type to (logically) contain itself? A visitor never sees the recursive_wrapper, for example, and the never-empty guarantee doesn't says that the variant always contains one of the types it can logically contain. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On 1/31/13 9:31 AM, Dave Abrahams wrote:
on Wed Jan 30 2013, Joel de Guzman <djowel-AT-gmail.com> wrote:
On 1/30/13 1:25 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Tue, Jan 29, 2013 at 7:12 AM, Paul Smith <pl.smith.mail@gmail.com> wrote:
This discussion might be facilitated if Joel et al (sorry Joel, I don't mean to pick on you, I just mean the group arguing for introducing this "singular" post-move state) simply said "yes, we understand we're making a breaking change (by possibly introducing an additional state to variant that violates the never-empty guarantee), but we still think it's the most practical approach to introduce efficient move semantics to variant". I can jive with that but I think Paul's concerned that you (again, as a representative of the platform you're taking) don't appreciate that this is a breaking change to variant.
No, Jeff, that is wrong. We are not violating the semantics to variant. It's not about variant. It's about recursive_wrapper. I think people are confused with this. The variant's never-empty guarantee still holds. A variant holding a singular-valued recursive_wrapper is similar to a variant holding a singular valued iterator. The variant is not empty.
But Joel, IIUC a variant never actually, from the abstract interface point-of-view, contains a recursive_wrapper anyway. Isn't the recursive_wrapper just a detail required to make it possible for a variant type to (logically) contain itself? A visitor never sees the recursive_wrapper, for example, and the never-empty guarantee doesn't says that the variant always contains one of the types it can logically contain.
Agreed. If you read my later reply, I am now agreeing with Jeff. I was wrong. Jeff is right. I see that now. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

on Mon Jan 28 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
This issue has been discussed more than once before, and nothing I say here is my own opinion, so please don't take it out on me. For example, see N3264 (CH-18 and US-85: Clarifying the state of moved-from objects (Revision 1)):
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3264.htm
and Dave confirms that,
I don't want to read into what Dave said too much, because he's here and he can clarify it. But I believe what he said is that specific algorithms, in their own localized context, practically only require destructibility and assignability. And even then, it's not something the standard actually guarantees in general, though, and the requirements are still much stricter, perhaps superfluously so. That's frustrating, no doubt about it, and it's a good selling point for having destructive move semantics - not for breaking the rules.
That doesn't sound like anything I meant to say, but I do agree fully with the resolutions (if not the NB comments) in the paper cited above. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On Thu, Jan 31, 2013 at 3:22 AM, Dave Abrahams <dave@boostpro.com> wrote:
on Mon Jan 28 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
This issue has been discussed more than once before, and nothing I say here is my own opinion, so please don't take it out on me. For example, see N3264 (CH-18 and US-85: Clarifying the state of moved-from objects (Revision 1)):
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3264.htm
and Dave confirms that,
I don't want to read into what Dave said too much, because he's here and he can clarify it. But I believe what he said is that specific algorithms, in their own localized context, practically only require destructibility and assignability. And even then, it's not something the standard actually guarantees in general, though, and the requirements are still much stricter, perhaps superfluously so. That's frustrating, no doubt about it, and it's a good selling point for having destructive move semantics - not for breaking the rules.
That doesn't sound like anything I meant to say, but I do agree fully with the resolutions (if not the NB comments) in the paper cited above.
Then what did you mean? I was responding to Joel's claim:
The standard only mentions assign and destroy as necessary, and Dave confirms that
He refers to you answer:
(The) ability to both assign and destroy? Is that all that it needs? That's all the standard library will use.
But the library's requirement from moved-from user-types, as of this paper's resolution, is stricter. In [moveconstructible]: rv’s state is unspecified [ Note:rv must still meet the requirements of the library component that is using it. The operations listed in those requirements must work as specified whether rv has been moved from or not. —end note ] Nowhere does it say anything about just being destructible and assignable. Btw, the reason why I brought up this paper was because I couldn't help noticing the similarity between the case mentioned there and what we have here. The US NB was requesting that the standard would require standard-library types (or maybe types in general) only to be destructible and assignable after a move. The case they mentioned was containers that allocate their sentinel nodes dynamically. If the container has to remain valid after a move, the move constructor has to allocate sentinel nodes "for nothing". The resolution was, AFAICT: No. Moved-from library types should remain valid. If dynamically allocated sentinel nodes are your implementation strategy, you have to bite the bullet (Which is, btw, what the Microsoft implementation actually does, at least as of MSVC 10. Could someone please check if they changed it in 11? Run the attached file, you should see two allocations). The resolution wasn't "let's say that moved-from types should only remain destructible and assignable", and it wasn't "let's add a singular value to std::list". Ofcourse, boost isn't the standard library and you're not bound by any of this. Still, it's awkward to have the standard pull in one direction and boost pull in the other one. -- Paul Smith

on Thu Jan 31 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Thu, Jan 31, 2013 at 3:22 AM, Dave Abrahams <dave@boostpro.com> wrote:
on Mon Jan 28 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
This issue has been discussed more than once before, and nothing I say here is my own opinion, so please don't take it out on me.
For example, see N3264 (CH-18 and US-85: Clarifying the state of moved-from objects (Revision 1)):
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3264.htm
and Dave confirms that,
I don't want to read into what Dave said too much, because he's here and he can clarify it. But I believe what he said is that specific algorithms, in their own localized context, practically only require destructibility and assignability. And even then,
I was disavowing this part because I don't claim to know it for sure:
it's not something the standard actually guarantees in general, though, and the requirements are still much stricter, perhaps superfluously so.
and I was disavowing this part because I disagree with it:
it's a good selling point for having destructive move semantics -
That doesn't sound like anything I meant to say, but I do agree fully with the resolutions (if not the NB comments) in the paper cited above.
Then what did you mean?
See above.
Btw, the reason why I brought up this paper was because I couldn't help noticing the similarity between the case mentioned there and what we have here. The US NB was requesting that the standard would require standard-library types (or maybe types in general) only to be destructible and assignable after a move. The case they mentioned was containers that allocate their sentinel nodes dynamically. If the container has to remain valid after a move, the move constructor has to allocate sentinel nodes "for nothing".
The resolution was, AFAICT: No. Moved-from library types should remain valid. If dynamically allocated sentinel nodes are your implementation strategy, you have to bite the bullet (Which is, btw, what the Microsoft implementation actually does, at least as of MSVC 10. Could someone please check if they changed it in 11? Run the attached file, you should see two allocations).
The resolution wasn't "let's say that moved-from types should only remain destructible and assignable", and it wasn't "let's add a singular value to std::list".
Right. And that's the resolution I fought for.
Ofcourse, boost isn't the standard library and you're not bound by any of this. Still, it's awkward to have the standard pull in one direction and boost pull in the other one.
I agree, it would be. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On Thu, Jan 31, 2013 at 10:11 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Thu Jan 31 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Thu, Jan 31, 2013 at 3:22 AM, Dave Abrahams <dave@boostpro.com> wrote:
on Mon Jan 28 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
This issue has been discussed more than once before, and nothing I say here is my own opinion, so please don't take it out on me.
For example, see N3264 (CH-18 and US-85: Clarifying the state of moved-from objects (Revision 1)):
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3264.htm
and Dave confirms that,
I don't want to read into what Dave said too much, because he's here and he can clarify it. But I believe what he said is that specific algorithms, in their own localized context, practically only require destructibility and assignability. And even then,
I was disavowing this part because I don't claim to know it for sure:
it's not something the standard actually guarantees in general, though, and the requirements are still much stricter, perhaps superfluously so.
and I was disavowing this part because I disagree with it:
it's a good selling point for having destructive move semantics -
That doesn't sound like anything I meant to say, but I do agree fully with the resolutions (if not the NB comments) in the paper cited above.
Then what did you mean?
See above.
Actually, that's not really what I asked. Joel asked you if destructibility and assignability is all the standard library needs from moved-from objects. You responded: "That's all the standard library will use." Now you seem to say the opposite. I think this was a source of confusion :-) -- Paul Smith

on Thu Jan 31 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Thu, Jan 31, 2013 at 10:11 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Thu Jan 31 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Thu, Jan 31, 2013 at 3:22 AM, Dave Abrahams <dave@boostpro.com> wrote:
on Mon Jan 28 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
This issue has been discussed more than once before, and nothing I say here is my own opinion, so please don't take it out on me.
For example, see N3264 (CH-18 and US-85: Clarifying the state of moved-from objects (Revision 1)):
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3264.htm
and Dave confirms that,
I don't want to read into what Dave said too much, because he's here and he can clarify it. But I believe what he said is that specific algorithms, in their own localized context, practically only require destructibility and assignability. And even then,
I was disavowing this part because I don't claim to know it for sure:
it's not something the standard actually guarantees in general, though, and the requirements are still much stricter, perhaps superfluously so.
and I was disavowing this part because I disagree with it:
it's a good selling point for having destructive move semantics -
That doesn't sound like anything I meant to say, but I do agree fully with the resolutions (if not the NB comments) in the paper cited above.
Then what did you mean?
See above.
Actually, that's not really what I asked. Joel asked you if destructibility and assignability is all the standard library needs from moved-from objects. You responded: "That's all the standard library will use."
Yes.
Now you seem to say the opposite.
Where?
I think this was a source of confusion :-)
I can imagine it would be. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On Fri, Feb 1, 2013 at 4:57 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Thu Jan 31 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Thu, Jan 31, 2013 at 10:11 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Thu Jan 31 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Thu, Jan 31, 2013 at 3:22 AM, Dave Abrahams <dave@boostpro.com> wrote:
on Mon Jan 28 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
This issue has been discussed more than once before, and nothing I say here is my own opinion, so please don't take it out on me.
For example, see N3264 (CH-18 and US-85: Clarifying the state of moved-from objects (Revision 1)):
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3264.htm
> and Dave confirms that,
I don't want to read into what Dave said too much, because he's here and he can clarify it. But I believe what he said is that specific algorithms, in their own localized context, practically only require destructibility and assignability. And even then,
I was disavowing this part because I don't claim to know it for sure:
it's not something the standard actually guarantees in general, though, and the requirements are still much stricter, perhaps superfluously so.
and I was disavowing this part because I disagree with it:
it's a good selling point for having destructive move semantics -
That doesn't sound like anything I meant to say, but I do agree fully with the resolutions (if not the NB comments) in the paper cited above.
Then what did you mean?
See above.
Actually, that's not really what I asked. Joel asked you if destructibility and assignability is all the standard library needs from moved-from objects. You responded: "That's all the standard library will use."
Yes.
Now you seem to say the opposite.
Where?
I think this was a source of confusion :-)
I can imagine it would be.
Okay, I'm truely confused about what you're saying then. Either the standard library requires moved-from user-types to keep meeting all their requirements, or it only requires them to remain destructible and assignable. The standard says the former. The wording that appears in the standard is the same (or almost the same) wording as the resolution of N3264, which is what you say you fought for. The only other way I can interpret "That's (destructibility and assignability) all that srandard library will use" is "that's all it will use in practice", but you say you disavow that. So, please, could you rephrase this sentence in a more elaborate way? -- Paul Smith

On 02/01/13 09:20, Paul Smith wrote:
On Fri, Feb 1, 2013 at 4:57 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Thu Jan 31 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Thu, Jan 31, 2013 at 10:11 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Thu Jan 31 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Thu, Jan 31, 2013 at 3:22 AM, Dave Abrahams <dave@boostpro.com> wrote:
on Mon Jan 28 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
> This issue has been discussed more than once before, and nothing I say > here is my own opinion, so please don't take it out on me.
> For example, see N3264 (CH-18 and US-85: Clarifying the state of > moved-from objects (Revision 1)): > > http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3264.htm > >> and Dave confirms that, > > I don't want to read into what Dave said too much, because he's here > and he can clarify it. But I believe what he said is that specific > algorithms, in their own localized context, practically only require > destructibility and assignability. And even then,
I was disavowing this part because I don't claim to know it for sure:
> it's not something the standard actually guarantees in general, > though, and the requirements are still much stricter, perhaps > superfluously so.
and I was disavowing this part because I disagree with it:
> it's a good selling point for having destructive move semantics -
That doesn't sound like anything I meant to say, but I do agree fully with the resolutions (if not the NB comments) in the paper cited above.
Then what did you mean?
See above.
Actually, that's not really what I asked. Joel asked you if destructibility and assignability is all the standard library needs from moved-from objects. You responded: "That's all the standard library will use."
Yes.
Now you seem to say the opposite.
Where?
I think this was a source of confusion :-)
I can imagine it would be.
Okay, I'm truely confused about what you're saying then.
Either the standard library requires moved-from user-types to keep meeting all their requirements, or it only requires them to remain destructible and assignable. The standard says the former. The wording that appears in the standard is the same (or almost the same) wording as the resolution of N3264, which is what you say you fought for.
The only other way I can interpret "That's (destructibility and assignability) all that srandard library will use" is "that's all it will use in practice", but you say you disavow that. [snip] Could you please give a link to the post where Dave disavows that and give a quote of the sentence where that's made? It would save other readers a good bit of time searching for this since this has been a *long* thread.
TIA. -regards, Larry

On Fri, Feb 1, 2013 at 6:06 PM, Larry Evans <cppljevans@suddenlink.net> wrote:
On 02/01/13 09:20, Paul Smith wrote:
On Fri, Feb 1, 2013 at 4:57 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Thu Jan 31 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Thu, Jan 31, 2013 at 10:11 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Thu Jan 31 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Thu, Jan 31, 2013 at 3:22 AM, Dave Abrahams <dave@boostpro.com> wrote: >
> on Mon Jan 28 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote: > >> This issue has been discussed more than once before, and nothing I say >> here is my own opinion, so please don't take it out on me.
>> For example, see N3264 (CH-18 and US-85: Clarifying the state of >> moved-from objects (Revision 1)): >> >> http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2011/n3264.htm >> >>> and Dave confirms that, >> >> I don't want to read into what Dave said too much, because he's here >> and he can clarify it. But I believe what he said is that specific >> algorithms, in their own localized context, practically only require >> destructibility and assignability. And even then,
I was disavowing this part because I don't claim to know it for sure:
>> it's not something the standard actually guarantees in general, >> though, and the requirements are still much stricter, perhaps >> superfluously so.
and I was disavowing this part because I disagree with it:
>> it's a good selling point for having destructive move semantics - > > That doesn't sound like anything I meant to say, but I do agree fully > with the resolutions (if not the NB comments) in the paper cited above.
Then what did you mean?
See above.
Actually, that's not really what I asked. Joel asked you if destructibility and assignability is all the standard library needs from moved-from objects. You responded: "That's all the standard library will use."
Yes.
Now you seem to say the opposite.
Where?
I think this was a source of confusion :-)
I can imagine it would be.
Okay, I'm truely confused about what you're saying then.
Either the standard library requires moved-from user-types to keep meeting all their requirements, or it only requires them to remain destructible and assignable. The standard says the former. The wording that appears in the standard is the same (or almost the same) wording as the resolution of N3264, which is what you say you fought for.
The only other way I can interpret "That's (destructibility and assignability) all that srandard library will use" is "that's all it will use in practice", but you say you disavow that. [snip] Could you please give a link to the post where Dave disavows that and give a quote of the sentence where that's made? It would save other readers a good bit of time searching for this since this has been a *long* thread.
It's been a long thread alright :-) There's the post: http://boost.2283326.n4.nabble.com/variant-Please-vote-for-behavior-Was-Basi... And there's the quote:
I don't want to read into what Dave said too much, because he's here and he can clarify it. But I believe what he said is that specific algorithms, in their own localized context, practically only require destructibility and assignability. And even then,
I was disavowing this part because I don't claim to know it for sure:
Anyway, I'm not sure my exposition of that was very clear either. That's why I'd like Dave to explain it in more detail (ignore what I said you said), instead of me trying to interpret it. -- Paul Smith

on Fri Feb 01 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Fri, Feb 1, 2013 at 6:06 PM, Larry Evans <cppljevans@suddenlink.net> wrote:
On 02/01/13 09:20, Paul Smith wrote:
On Fri, Feb 1, 2013 at 4:57 PM, Dave Abrahams <dave@boostpro.com> wrote:
There's the post: http://boost.2283326.n4.nabble.com/variant-Please-vote-for-behavior-Was-Basi...
And there's the quote:
I don't want to read into what Dave said too much, because he's here and he can clarify it. But I believe what he said is that specific algorithms, in their own localized context, practically only require destructibility and assignability. And even then,
I was disavowing this part because I don't claim to know it for sure:
Anyway, I'm not sure my exposition of that was very clear either. That's why I'd like Dave to explain it in more detail (ignore what I said you said), instead of me trying to interpret it.
The colon is meant to indicate that the statement applies to what follows, not what comes before. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On Fri, Feb 1, 2013 at 7:16 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Fri Feb 01 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Fri, Feb 1, 2013 at 6:06 PM, Larry Evans <cppljevans@suddenlink.net> wrote:
On 02/01/13 09:20, Paul Smith wrote:
On Fri, Feb 1, 2013 at 4:57 PM, Dave Abrahams <dave@boostpro.com> wrote:
There's the post: http://boost.2283326.n4.nabble.com/variant-Please-vote-for-behavior-Was-Basi...
And there's the quote:
I don't want to read into what Dave said too much, because he's here and he can clarify it. But I believe what he said is that specific algorithms, in their own localized context, practically only require destructibility and assignability. And even then,
I was disavowing this part because I don't claim to know it for sure:
Anyway, I'm not sure my exposition of that was very clear either. That's why I'd like Dave to explain it in more detail (ignore what I said you said), instead of me trying to interpret it.
The colon is meant to indicate that the statement applies to what follows, not what comes before.
Ofcourse. Missed it :-P Thanks! -- Paul Smith

on Fri Feb 01 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Fri, Feb 1, 2013 at 4:57 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Thu Jan 31 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
Actually, that's not really what I asked. Joel asked you if destructibility and assignability is all the standard library needs from moved-from objects. You responded: "That's all the standard library will use."
Yes.
Now you seem to say the opposite.
Where?
I think this was a source of confusion :-)
I can imagine it would be.
Okay, I'm truely confused about what you're saying then.
Either the standard library requires moved-from user-types to keep meeting all their requirements, or it only requires them to remain destructible and assignable.
Not knowing if there's specific text about this, I can't say for sure. If, as I think is likely, what it requires of them is not explicitly called out, then it is implied by other requirements. In other words, the library has the freedom to perform any operations on such objects as it may perform on any other object in the same context. For example, in a call to sort(first, last), it is free to compare two objects in the range [first,last) even if one or both of them has been moved-from, so the objects passed to such a call must, technically, preserve less-than-comparability of moved-from objects. In practice, though, IIUC implementations will only actually use assignment and destruction on moved-from objects.
The standard says the former.
If you say so.
The wording that appears in the standard is the same (or almost the same) wording as the resolution of N3264, which is what you say you fought for.
OK.
The only other way I can interpret "That's (destructibility and assignability) all that srandard library will use" is "that's all it will use in practice", but you say you disavow that.
No, I only disavow these parts of what you attributed to me: "it's not something the standard actually guarantees in general, though, and the requirements are still much stricter, perhaps superfluously so. ... it's a good selling point for having destructive move semantics."
So, please, could you rephrase this sentence in a more elaborate way?
HTH, -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On Fri, Feb 1, 2013 at 7:15 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Fri Feb 01 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
Okay, I'm truely confused about what you're saying then.
Either the standard library requires moved-from user-types to keep meeting all their requirements, or it only requires them to remain destructible and assignable.
Not knowing if there's specific text about this, I can't say for sure. If, as I think is likely, what it requires of them is not explicitly called out, then it is implied by other requirements. In other words, the library has the freedom to perform any operations on such objects as it may perform on any other object in the same context. For example, in a call to sort(first, last), it is free to compare two objects in the range [first,last) even if one or both of them has been moved-from, so the objects passed to such a call must, technically, preserve less-than-comparability of moved-from objects.
In practice, though, IIUC implementations will only actually use assignment and destruction on moved-from objects.
This was my understanding.
The standard says the former.
If you say so.
Table 20, [moveconstructible]: rv (the moved-from object) must still meet the requirements of the library component that is using it. The operations listed in those requirements must work as specified whether rv has been moved from or not. How else should I read it? When I say that "this is what the library requires" I mean exactly that - what it technically requires, not what it will or will not use in practice.
The wording that appears in the standard is the same (or almost the same) wording as the resolution of N3264, which is what you say you fought for.
OK.
The only other way I can interpret "That's (destructibility and assignability) all that srandard library will use" is "that's all it will use in practice", but you say you disavow that.
No, I only disavow these parts of what you attributed to me:
"it's not something the standard actually guarantees in general, though, and the requirements are still much stricter, perhaps superfluously so.
I'm not sure what you're disavowing then. You don't say that the standard guarantees that destructibility and assignability are enough, but you do say that this is all it will need in practice. That's called superfluous in my book. I'm not saying that it's a good thing or a bad thing.
... it's a good selling point for having destructive move semantics."
So, please, could you rephrase this sentence in a more elaborate way?
HTH,
Indeed! And sorry for nagging. -- Paul Smith

on Fri Feb 01 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
On Fri, Feb 1, 2013 at 7:15 PM, Dave Abrahams <dave@boostpro.com> wrote:
on Fri Feb 01 2013, Paul Smith <pl.smith.mail-AT-gmail.com> wrote:
Okay, I'm truely confused about what you're saying then.
Either the standard library requires moved-from user-types to keep meeting all their requirements, or it only requires them to remain destructible and assignable.
Not knowing if there's specific text about this, I can't say for sure. If, as I think is likely, what it requires of them is not explicitly called out, then it is implied by other requirements. In other words, the library has the freedom to perform any operations on such objects as it may perform on any other object in the same context. For example, in a call to sort(first, last), it is free to compare two objects in the range [first,last) even if one or both of them has been moved-from, so the objects passed to such a call must, technically, preserve less-than-comparability of moved-from objects.
In practice, though, IIUC implementations will only actually use assignment and destruction on moved-from objects.
This was my understanding.
The standard says the former.
If you say so.
Table 20, [moveconstructible]:
rv (the moved-from object) must still meet the requirements of the library component that is using it. The operations listed in those requirements must work as specified whether rv has been moved from or not.
How else should I read it? When I say that "this is what the library requires" I mean exactly that - what it technically requires, not what it will or will not use in practice.
I think you read it correctly. However, be aware that the text in question is in a non-normative note, which means that it doesn't change the meaning of the standard. Those facts are implied by the other text; in particular, the lack of any special and explicit permission for moved-from objects to stop meeting their requirements.
No, I only disavow these parts of what you attributed to me:
"it's not something the standard actually guarantees in general, though, and the requirements are still much stricter, perhaps superfluously so.
I'm not sure what you're disavowing then. You don't say that the standard guarantees that destructibility and assignability are enough, but you do say that this is all it will need in practice. That's called superfluous in my book.
That's a judgement that you're willing to make, but I'm not. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On 01/27/13 18:02, Joel de Guzman wrote:
On 1/28/13 12:18 AM, Dave Abrahams wrote:
on Sat Jan 26 2013, Joel de Guzman <djowel-AT-gmail.com> wrote:
On 1/26/13 7:58 AM, Dave Abrahams wrote:
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.
(The) ability to both assign and destroy? Is that all that it needs?
That's all the standard library will use. However, you still have to be honest about the variant's invariant: it must include the moved-from state.
Then we should be OK. A nulled recursive_wrapper can both be assigned and destroyed. It just can't do other things apart from that, such as get the underlying T& and call its member functions.
I am not familiar enough with the details of recursive_wrapper and how much of it is exposed to users to know whether any of this matters. You have to choose: either the addition of this moved-from state must not invalidate any previous guarantees upon which users may rely, OR you have to tell them that you're breaking backward-compatibility and document that the moved-from state is an "anti-precondition" for all the "other things apart from that."
Or you could invent a whole new concept of "invariant" that is allowed to be violated... but I really discourage that! :-)
I think we are OK!
Come to think of it, the situation is a lot like a "singular" iterator:
"Iterators can also have singular values that are not associated with any sequence. [snip] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation."
Indeed, for a singular valued iterator, i, you can assign to i and destruct i, place i in a container, etc. You just cannot dereference i, access its underlying value through ->, compare i with another iterator, etc.
The same is true with a nulled recursive_wrapper.
[snip] What would a visitor do in this case? Currently, it requires a non-null recursive_wrapper, IIUC. If a nulled recursive_wrapper were allowed, would the visitors then need to supply: template<T> operator()(recursive_wrapper<T>const& rw) and the user would have to test rw::_p? -regards, Larry

On 1/29/13 2:18 AM, Larry Evans wrote:
On 01/27/13 18:02, Joel de Guzman wrote:
On 1/28/13 12:18 AM, Dave Abrahams wrote:
on Sat Jan 26 2013, Joel de Guzman <djowel-AT-gmail.com> wrote:
On 1/26/13 7:58 AM, Dave Abrahams wrote:
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.
(The) ability to both assign and destroy? Is that all that it needs?
That's all the standard library will use. However, you still have to be honest about the variant's invariant: it must include the moved-from state.
Then we should be OK. A nulled recursive_wrapper can both be assigned and destroyed. It just can't do other things apart from that, such as get the underlying T& and call its member functions.
I am not familiar enough with the details of recursive_wrapper and how much of it is exposed to users to know whether any of this matters. You have to choose: either the addition of this moved-from state must not invalidate any previous guarantees upon which users may rely, OR you have to tell them that you're breaking backward-compatibility and document that the moved-from state is an "anti-precondition" for all the "other things apart from that."
Or you could invent a whole new concept of "invariant" that is allowed to be violated... but I really discourage that! :-)
I think we are OK!
Come to think of it, the situation is a lot like a "singular" iterator:
"Iterators can also have singular values that are not associated with any sequence. [snip] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation."
Indeed, for a singular valued iterator, i, you can assign to i and destruct i, place i in a container, etc. You just cannot dereference i, access its underlying value through ->, compare i with another iterator, etc.
The same is true with a nulled recursive_wrapper.
[snip] What would a visitor do in this case? Currently, it requires a non-null recursive_wrapper, IIUC. If a nulled recursive_wrapper were allowed, would the visitors then need to supply:
template<T> operator()(recursive_wrapper<T>const& rw)
and the user would have to test rw::_p?
No. It's still just operator(T const&). You can't use a "singular valued" recursive_wrapper in any other operation apart from assignment, destruction or move. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Mon, Jan 28, 2013 at 1:35 PM, Joel de Guzman <djowel@gmail.com> wrote
No. It's still just operator(T const&). You can't use a "singular
valued" recursive_wrapper in any other operation apart from assignment, destruction or move.
How do I know if is it singular valued? Do I need to check everywhere? Basically, can you guarantee the never "leak out" and are destroyed or assigned immediately after the move?

On 1/29/13 5:06 AM, Gottlob Frege wrote:
On Mon, Jan 28, 2013 at 1:35 PM, Joel de Guzman <djowel@gmail.com> wrote
No. It's still just operator(T const&). You can't use a "singular
valued" recursive_wrapper in any other operation apart from assignment, destruction or move.
How do I know if is it singular valued?
How do you know if an iterator is singular valued? You can't. Same here. Do I need to check everywhere? No, you don't have to. You don't need to.
Basically, can you guarantee the never "leak out" and are destroyed or assigned immediately after the move?
No, you can't guarantee that, and you don't disallow that. But likewise, neither can you guarantee that a singular valued iterator leaks out into the wild. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Mon, Jan 28, 2013 at 4:47 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/29/13 5:06 AM, Gottlob Frege wrote:
On Mon, Jan 28, 2013 at 1:35 PM, Joel de Guzman <djowel@gmail.com> wrote
No. It's still just operator(T const&). You can't use a "singular
valued" recursive_wrapper in any other operation apart from assignment, destruction or move.
How do I know if is it singular valued?
How do you know if an iterator is singular valued? You can't. Same here.
it == container.end()? Or am I confused?
Do I need to check everywhere?
No, you don't have to. You don't need to.
Basically, can you guarantee the never "leak out" and are destroyed or
assigned immediately after the move?
No, you can't guarantee that, and you don't disallow that. But likewise, neither can you guarantee that a singular valued iterator leaks out into the wild.
Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On 1/29/13 5:58 AM, Gottlob Frege wrote:
How do I know if is it singular valued?
How do you know if an iterator is singular valued? You can't. Same here.
it == container.end()?
Or am I confused?
Nope. A singular valued iterator may not be compared. A singular valued iterator is not associated with any sequence. E.g default constructed iterator pointing to nowhere. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On 1/29/13 5:58 AM, Gottlob Frege wrote:
How do I know if is it singular valued?
How do you know if an iterator is singular valued? You can't. Same here.
it == container.end()?
Or am I confused?
Nope. A singular valued iterator may not be compared. A singular valued iterator is not associated with any sequence. E.g default constructed iterator pointing to nowhere.
Regards, Why impose the same limitation on recursive_wrapper<T>. Just because
On 01/28/13 16:12, Joel de Guzman wrote: the current variant visitor requires the recursive_wrapper<T> to be dereferenced doesn't mean that's the best practice. Why not, as suggested previously, the visitor protocol allow: operator()(recursive_wrapper<T>const&) and, for that matter have get<T> return a recursive_wrapper<T>& or recursive_wrapper<T>const&. The current variant doesn't do that for, I guess, convenience reasons; however, as evidenced by the current *long* discussion, that convenience is illusory, IMHO. Such a proposal was made previously here: http://lists.boost.org/Archives/boost/2013/01/200407.php but, for some reason, didn't have any responses.

On 1/29/13 11:18 AM, Larry Evans wrote:
On 1/29/13 5:58 AM, Gottlob Frege wrote:
How do I know if is it singular valued?
>
How do you know if an iterator is singular valued? You can't. Same here.
it == container.end()?
Or am I confused?
Nope. A singular valued iterator may not be compared. A singular valued iterator is not associated with any sequence. E.g default constructed iterator pointing to nowhere.
Regards, Why impose the same limitation on recursive_wrapper<T>. Just because
On 01/28/13 16:12, Joel de Guzman wrote: the current variant visitor requires the recursive_wrapper<T> to be dereferenced doesn't mean that's the best practice. Why not, as suggested previously, the visitor protocol allow:
operator()(recursive_wrapper<T>const&)
and, for that matter have get<T> return a recursive_wrapper<T>& or recursive_wrapper<T>const&. The current variant doesn't do that for, I guess, convenience reasons; however, as evidenced by the current *long* discussion, that convenience is illusory, IMHO.
Such a proposal was made previously here:
AFAICT, you can already do that with any smart pointer (e.g. unique_ptr<T>) Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Jan 28, 2013, at 5:12 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/29/13 5:58 AM, Gottlob Frege wrote:
How do I know if is it singular valued?
How do you know if an iterator is singular valued? You can't. Same here.
it == container.end()?
Or am I confused?
Nope. A singular valued iterator may not be compared. A singular valued iterator is not associated with any sequence. E.g default constructed iterator pointing to nowhere.
Singular iterators are default constructed instances that represent a universal end iterator. They are not associated with any particular container, but would be returned by end(), and can be obtained by advancing a non-end iterator far enough. Thus, Tony's snippet is a means to discover a singular instance, as would comparing with a default constructed iterator. Is the same now to be possible with recursive_wrapper? ___ Rob

On Tue, Jan 29, 2013 at 10:56 AM, Rob Stewart <robertstewart@comcast.net>wrote:
On Jan 28, 2013, at 5:12 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/29/13 5:58 AM, Gottlob Frege wrote:
How do I know if is it singular valued?
>
How do you know if an iterator is singular valued? You can't. Same here.
it == container.end()?
Or am I confused?
Nope. A singular valued iterator may not be compared. A singular valued iterator is not associated with any sequence. E.g default constructed iterator pointing to nowhere.
Singular iterators are default constructed instances that represent a universal end iterator.
This is only true for some iterators. Generally, comparing to default constructed iterators is UB; for example comparing to default initialized pointers. It is generally not possible to discover a singular iterator. HTH, -- gpd

On 1/29/13 6:56 PM, Rob Stewart wrote:
On Jan 28, 2013, at 5:12 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/29/13 5:58 AM, Gottlob Frege wrote:
How do I know if is it singular valued?
>
How do you know if an iterator is singular valued? You can't. Same here.
it == container.end()?
Or am I confused?
Nope. A singular valued iterator may not be compared. A singular valued iterator is not associated with any sequence. E.g default constructed iterator pointing to nowhere.
Singular iterators are default constructed instances that represent a universal end iterator. They are not associated with any particular container, but would be returned by end(), and can be obtained by advancing a non-end iterator far enough. Thus, Tony's snippet is a means to discover a singular instance, as would comparing with a default constructed iterator. Is the same now to be possible with recursive_wrapper?
Singular valued iterators cannot be compared (*). The only valid operations are assignment, destruction and move. There is no way to detect a singular valued iterator. (* Iterators can also have singular values that are not associated with any sequence. [ Example: After the declaration of an uninitialized pointer x (as with int* x;), x must always be assumed to have a singular value of a pointer. —end example ] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation.) Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Jan 29, 2013, at 7:04 AM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/29/13 6:56 PM, Rob Stewart wrote:
Singular iterators are default constructed instances that represent a universal end iterator. They are not associated with any particular container, but would be returned by end(), and can be obtained by advancing a non-end iterator far enough.
Singular valued iterators cannot be compared (*). The only valid operations are assignment, destruction and move. There is no way to detect a singular valued iterator.
(* Iterators can also have singular values that are not associated with any sequence. [ Example: After the declaration of an uninitialized pointer x (as with int* x;), x must always be assumed to have a singular value of a pointer. —end example ] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation.)
Oh, sure, slap me down with the standard! :) Seriously, I was thinking of, for example, std::istream_iterator. If the default constructor doesn't create a singular iterator, what is the right name? I've either forgotten it or long misapplied "singular" to such iterators. ___ Rob

On 1/30/13 6:50 AM, Rob Stewart wrote:
On Jan 29, 2013, at 7:04 AM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/29/13 6:56 PM, Rob Stewart wrote:
Singular iterators are default constructed instances that represent a universal end iterator. They are not associated with any particular container, but would be returned by end(), and can be obtained by advancing a non-end iterator far enough.
Singular valued iterators cannot be compared (*). The only valid operations are assignment, destruction and move. There is no way to detect a singular valued iterator.
(* Iterators can also have singular values that are not associated with any sequence. [ Example: After the declaration of an uninitialized pointer x (as with int* x;), x must always be assumed to have a singular value of a pointer. —end example ] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation.)
Oh, sure, slap me down with the standard! :)
Seriously, I was thinking of, for example, std::istream_iterator. If the default constructor doesn't create a singular iterator, what is the right name? I've either forgotten it or long misapplied "singular" to such iterators.
Oh right. Yeah, it will work for std::istream_iterator. In that case, no, it's not "singular valued". I think it's just default contructed, unless I am missing a name too. Some default constructed iterators (e.g. as you pointed out) do not result to being "singular valued". Cheers, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

on Mon Jan 28 2013, Gottlob Frege <gottlobfrege-AT-gmail.com> wrote:
On Mon, Jan 28, 2013 at 4:47 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/29/13 5:06 AM, Gottlob Frege wrote:
On Mon, Jan 28, 2013 at 1:35 PM, Joel de Guzman <djowel@gmail.com> wrote
No. It's still just operator(T const&). You can't use a "singular
valued" recursive_wrapper in any other operation apart from assignment, destruction or move.
How do I know if is it singular valued?
How do you know if an iterator is singular valued? You can't. Same here.
it == container.end()?
Or am I confused?
Yes, you are. That's not singular; it's a past-the-end iterator. You can still do comparisons against a past-the-end iterator, but not with a singular one. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

on Mon Jan 28 2013, Joel de Guzman <djowel-AT-gmail.com> wrote:
On 1/28/13 12:18 AM, Dave Abrahams wrote:
on Sat Jan 26 2013, Joel de Guzman <djowel-AT-gmail.com> wrote:
I think we are OK!
Come to think of it, the situation is a lot like a "singular" iterator:
"Iterators can also have singular values that are not associated with any sequence. [snip] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation."
Indeed, for a singular valued iterator, i, you can assign to i and destruct i, place i in a container, etc. You just cannot dereference i, access its underlying value through ->, compare i with another iterator, etc.
Yes, it's like a singular iterator, but it's a lot more like a null pointer. A singular iterator is like (and may in fact be) an uninitialized pointer, over which the program does not and cannot exert any control. A null pointer, on the other hand, has been initialized to a known state, but one in which some operations (e.g. *p, ++p, --p, p[0], ...) are not allowed. Or you could think of it as being like the a zero-valued primitive numeric type, which you can't use as a divisor.
The same is true with a nulled recursive_wrapper.
Being honest about this is just a matter of documentation. I do not see any problem at all with having a "singular" recursive_wrapper value.
Does it break backward-compatibility, by changing the guarantees users have about the possible values of certain variant types? If so, the documentation should make that clear as well. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On 1/31/13 12:16 AM, Dave Abrahams wrote:
on Mon Jan 28 2013, Joel de Guzman <djowel-AT-gmail.com> wrote:
On 1/28/13 12:18 AM, Dave Abrahams wrote:
on Sat Jan 26 2013, Joel de Guzman <djowel-AT-gmail.com> wrote:
I think we are OK!
Come to think of it, the situation is a lot like a "singular" iterator:
"Iterators can also have singular values that are not associated with any sequence. [snip] Results of most expressions are undefined for singular values; the only exceptions are destroying an iterator that holds a singular value, the assignment of a non-singular value to an iterator that holds a singular value, and, for iterators that satisfy the DefaultConstructible requirements, using a value-initialized iterator as the source of a copy or move operation."
Indeed, for a singular valued iterator, i, you can assign to i and destruct i, place i in a container, etc. You just cannot dereference i, access its underlying value through ->, compare i with another iterator, etc.
Yes, it's like a singular iterator, but it's a lot more like a null pointer. A singular iterator is like (and may in fact be) an uninitialized pointer, over which the program does not and cannot exert any control. A null pointer, on the other hand, has been initialized to a known state, but one in which some operations (e.g. *p, ++p, --p, p[0], ...) are not allowed. Or you could think of it as being like the a zero-valued primitive numeric type, which you can't use as a divisor.
The same is true with a nulled recursive_wrapper.
Being honest about this is just a matter of documentation. I do not see any problem at all with having a "singular" recursive_wrapper value.
Does it break backward-compatibility, by changing the guarantees users have about the possible values of certain variant types? If so, the documentation should make that clear as well.
Thank you Dave for your insights. That is sensible and reasonable. In the end, it's all about the variant's invariant alright :-P. I can see now how it may break backward compatibility. Yet, that would be an extremely rare situation and IMO, the practicality of a highly efficient move far outweighs this backward-compatibility issue. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Fri, Jan 25, 2013 at 3:16 PM, Krzysztof Czainski <1czajnik@gmail.com>wrote:
2013/1/25 Gottlob Frege <gottlobfrege@gmail.com>
On Fri, Jan 25, 2013 at 10:21 AM, Dave Abrahams <dave@boostpro.com> wrote:
Isn't leaving the class with it's invariants broken simply a defect?
Yes. IIUC the question here is whether the invariant of variant [;-)] shall be weakened to accommodate efficient move semantics, thereby breaking some code, or not, at some cost (the specific costs to be incurred by various strategies presently under discussion).
But I have to admit, I haven't been reading the thread all that carefully, so I could be mis-understanding completely.
Instead of weakened, which is one option discussed, I would also characterize much of the discussion as "let's ignore that the invariant is broken, because no one should rely on invariants after move".
If you had opinions on that aspect, you might want to weigh in. It would be appreciated.
Tony
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.
int i = 17; std::move(i); cout << i; don't do that because you don't know what value it will print, or because it will crash, cause "undefined behaviour", overwrite your harddrive? There's a difference between "don't know what value" and "UB". Tony

2013/1/25 Gottlob Frege <gottlobfrege@gmail.com>
On Fri, Jan 25, 2013 at 3:16 PM, Krzysztof Czainski <1czajnik@gmail.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. int i = 17; std::move(i);
I think you mean: int other = std::move(i); Just calling move doesn't move yet ;-) But then for ints and other PODs nothing is moved in any case, so I think the answer is always 17 here.
cout << i;
don't do that because you don't know what value it will print, or because it will crash, cause "undefined behaviour", overwrite your harddrive?
There's a difference between "don't know what value" and "UB".
Right. So, is the following "UB" or "don't know what value"? int i; cout << i; I think it is officially the former, but in practice the latter, am I wrong? So to me the question is: should move introduce an "uninitialized" state for types that don't have one in the first place. Int has an uninitialized state. Iterators in std algorithms are allowed to have such a state too. And now we have moved-from objects - should they be allowed an additional "uninitialized" state? I answer yes, because moved-from objects must not be used for anything other than destruction or assignment-to. Kris

On Fri, Jan 25, 2013 at 4:25 PM, Krzysztof Czainski <1czajnik@gmail.com>wrote:
2013/1/25 Gottlob Frege <gottlobfrege@gmail.com>
On Fri, Jan 25, 2013 at 3:16 PM, Krzysztof Czainski <1czajnik@gmail.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. int i = 17; std::move(i);
I think you mean: int other = std::move(i);
yes, of course :-)
Just calling move doesn't move yet ;-) But then for ints and other PODs nothing is moved in any case, so I think the answer is always 17 here.
probably always true, although I don't know if it is written in stone (ie the standard).
cout << i;
don't do that because you don't know what value it will print, or because it will crash, cause "undefined behaviour", overwrite your harddrive?
There's a difference between "don't know what value" and "UB".
Right. So, is the following "UB" or "don't know what value"? int i; cout << i; I think it is officially the former, but in practice the latter, am I wrong?
So to me the question is: should move introduce an "uninitialized" state for types that don't have one in the first place. Int has an uninitialized state. Iterators in std algorithms are allowed to have such a state too. And now we have moved-from objects - should they be allowed an additional "uninitialized" state? I answer yes, because moved-from objects must not be used for anything other than destruction or assignment-to.
Kris
But I don't think this is the question - or it was, but no longer is - because I think the standard already asked and answered the question (and it was hotly debated, but answered) - the answer was that moved from objects should be in valid but unknown states. ie not UB. not uninitialized. We might wish it different, but it isn't. P.S. Now, really, the standard says very little about how your types behave - you can make them do whateve in response to move, but for std:: types and containers and algorithms that take your types it was agreed the behaviour should be valid but unknown.

2013/1/25 Gottlob Frege <gottlobfrege@gmail.com>
On Fri, Jan 25, 2013 at 4:25 PM, Krzysztof Czainski <1czajnik@gmail.com
wrote: So to me the question is: should move introduce an "uninitialized" state for types that don't have one in the first place. Int has an uninitialized state. Iterators in std algorithms are allowed to have such a state too. And now we have moved-from objects - should they be allowed an additional "uninitialized" state? I answer yes, because moved-from objects must not be used for anything other than destruction or assignment-to.
But I don't think this is the question - or it was, but no longer is - because I think the standard already asked and answered the question (and it was hotly debated, but answered) - the answer was that moved from objects should be in valid but unknown states. ie not UB. not uninitialized.
Oh, thanks for clarifying this. I thought the standard said, that moved from objects should be in a state valid only for destruction/assignment to. If I was wrong, and it says the moved from objects must also be usable in other ways (i.e. readable), then you just convinced me here ;-) And in this case, I am against adding an empty state to variant recursive wrapper. We might wish it different, but it isn't.
Cheers Kris

on Fri Jan 25 2013, Krzysztof Czainski <1czajnik-AT-gmail.com> wrote:
So to me the question is: should move introduce an "uninitialized" state for types that don't have one in the first place.
Only if you're willing to break code.
Int has an uninitialized state. Iterators in std algorithms are allowed to have such a state too. And now we have moved-from objects - should they be allowed an additional "uninitialized" state? I answer yes, because moved-from objects must not be used for anything other than destruction or assignment-to.
The part after "because" is your assertion, but is not an absolute truth. Furthermore, the destructor may depend on (e.g. check) the invariant you've broken by introducing the "uninitialized" state. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On Friday 25 January 2013 16:05:14 Gottlob Frege wrote:
On Fri, Jan 25, 2013 at 3:16 PM, Krzysztof Czainski <1czajnik@gmail.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.
int i = 17; std::move(i); cout << i;
don't do that because you don't know what value it will print, or because it will crash, cause "undefined behaviour", overwrite your harddrive?
There's a difference between "don't know what value" and "UB".
I think you're mixing two different things here. When it comes to uninitialized objects, there isn't any difference as far as the standard is concerned. Whether you try to use an uninitialized (e.g. not yet constructed) std::string or int it doesn't matter - either way it's undefined behavior and anything can happen. OTOH a moved-from object is not uninitialized, but its state is unspecified. The only thing that can be said for certain about it is that it is capable of being destroyed. I cannot find any other guarantees provided by the standard, so I'd consider any other use of such objects as undefined, unless such guarantees are explicitly provided by the interface documentation. Whether this moved-from state violates or supplements object's invariants is a rhethorical question, IMHO. The important part is that unless explicitly documented otherwise, one should not expect any well-defined behavior (including the ability of being reinitialized with assignment) from a moved- from object. As for the POD types, you can say they don't have a move constructor, so copy is used instead. This makes the use of moved-from PODs defined, but that's a special case.

on Fri Jan 25 2013, Krzysztof Czainski <1czajnik-AT-gmail.com> wrote:
2013/1/25 Gottlob Frege <gottlobfrege@gmail.com>
On Fri, Jan 25, 2013 at 10:21 AM, Dave Abrahams <dave@boostpro.com> wrote:
Isn't leaving the class with it's invariants broken simply a defect?
Yes. IIUC the question here is whether the invariant of variant [;-)] shall be weakened to accommodate efficient move semantics, thereby breaking some code, or not, at some cost (the specific costs to be incurred by various strategies presently under discussion).
But I have to admit, I haven't been reading the thread all that carefully, so I could be mis-understanding completely.
Instead of weakened, which is one option discussed, I would also characterize much of the discussion as "let's ignore that the invariant is broken, because no one should rely on invariants after move".
If you had opinions on that aspect, you might want to weigh in. It would be appreciated.
Tony
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.
Then you propose to weaken the invariant of variant and as a side-effect, break some existing code. That's fine, as long as you appreciate the consequences of that approach. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

on Fri Jan 25 2013, Gottlob Frege <gottlobfrege-AT-gmail.com> wrote:
On Fri, Jan 25, 2013 at 10:21 AM, Dave Abrahams <dave@boostpro.com> wrote:
Isn't leaving the class with it's invariants broken simply a defect?
Yes. IIUC the question here is whether the invariant of variant [;-)] shall be weakened to accommodate efficient move semantics, thereby breaking some code, or not, at some cost (the specific costs to be incurred by various strategies presently under discussion).
But I have to admit, I haven't been reading the thread all that carefully, so I could be mis-understanding completely.
Instead of weakened, which is one option discussed, I would also characterize much of the discussion as "let's ignore that the invariant is broken, because no one should rely on invariants after move".
By definition, the invariant can never be broken until the lifetime of the object ends, except during individual mutations to that object. If you "ignore the the invariant is broken," you've just mis-identified the true invariant.
If you had opinions on that aspect, you might want to weigh in. It would be appreciated.
If you want the concept of invariants to have any power at all, e.g. to help you reason about code, the invariant has to describe the actual possible states of the object. Anything else is worse than useless. -- Dave Abrahams BoostPro Computing Software Development Training http://www.boostpro.com Clang/LLVM/EDG Compilers C++ Boost

On 25 January 2013 14:58, Neil Groves <neil@grovescomputing.com> wrote:
My vote is that we shouldn't even consider allowing the class invariants for ordered containers to be broken. I cannot imagine a motivation for allowing them to be broken. I suspect this may be a symptom of my failure to comprehend part of this discussion.
Ordered containers have const keys, so the keys should never be modified (or have I missed something?).

2013/1/21 Antony Polukhin <antoshkka@gmail.com>
Current implementation of recursive_wrapper move constructor is not optimal:
recursive_wrapper<T>::recursive_wrapper(recursive_wrapper&& operand) : p_(new T(std::move(operand.get()) )) { }
During descussion were made following proposals:
[...] II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations
I vote for II, because: + good performance
+ provides noexcept guarantee for move constructor + optimization will be used even if varinat has no type with trivial default constructor + easy to implement
And the above 4 points mean to me that it is a simple solution, which does the job. - triggers an assert when user tries to reuse moved object
Yes, using the moved object in other cases then assigning something to it is UB, so that's the right thing to do ;-) - adds an empty state to the recursive_wrapper This empty state can only be achieved by moving from the object, right? That's fine with me. Regards, Kris

On Mon, Jan 21, 2013 at 3:39 PM, Antony Polukhin <antoshkka@gmail.com> wrote:
Current implementation of recursive_wrapper move constructor is not optimal:
recursive_wrapper<T>::recursive_wrapper(recursive_wrapper&& operand) : p_(new T(std::move(operand.get()) )) { }
First of all, let's make a distinction here: there's recursive_wrapper, and there's a variant that contains a recursive_wrapper. Peter's solution, for example, addresses the latter, not the former.
During descussion were made following proposals:
I: Leave it as is - bad performance - can not provide noexcept guarantee
I take it as leave variant as is. I think we're already past that.
II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations + good performance + provides noexcept guarantee for move constructor
recursive_wrapper's move constructor. Not necessarily variant's.
+ optimization will be used even if varinat has no type with trivial default constructor
And with a standalone recursive_wrapper.
+ easy to implement - triggers an assert when user tries to reuse moved object - adds an empty state to the recursive_wrapper
Does it? Either recursive_wrapper has a documented, valid empty state, or a "gee I hope noone ever steps here" pseudo-"state".
III: Make recursive_wrapper and variant cooperate, enable move for varinat in the presence of recursive_wrappers only when there is at least one type that is nothrow-default-constructible, regardless of whether it's current.
We can enable move construction regardless. The presence of a cheap type simply enables this optimization.
It is easyer to understand by example: typedef variant<int, recursive_wrapper<foo>> V; V v1( std::move(v2) ); This move-constructs v1 from v2 and leaves int() into v2. + good performance + provides noexcept guarantee for rcursive_wrapper
No, not for recursive_wrapper. recursive_wrapper's move constructor remains as-is. It is only when recursive_wrapper is hosted inside a variant that we can do this. And even then, if any of the other types has a throwing move/copy-ctor, variant can't have a noexcept move-ctor (which is true for the other solutions as well).
+ does not adds an empty state - optimization won't trigger if varinat has no type with trivial default constructor
And not with a standalone recursive_wrapper.
- hard to implement
Well, hardER. Variant already does something similar.
- user may be obscured by the fact, that v2 from the example now contains int
Not really. That's what a move constructor does. If the user wants copy semantics he shouldn't move the variant.
IV: After move away, delay construction of type held by recursive_wrapper till it will be be required. +/- good performance? but more checks
More checks, dynamic allocation and possibly throwing in 'get'. Not a very good thing for a previously trivial inline function. Not sure at all that this is an overall win performance-wise.
+ provides noexcept guarantee for rcursive_wrapper
And provides yesexcept for 'get' :-)
+ does not adds an explicit empty state + optimization will be used even if varinat has no type with trivial default constructor +/- not hard to implement --- additional requirement for type T of recursive_wrapper: it must be default constructible
+1
- less obvious behavior for user (for example get() function would construct values, allocate memory and can possibly throw)
Please, vote for solution or propose a better one.
Solution 2, and any of its spinoffs, is just passing responsibility from one point to the other. It's definitely not a "correct" solution. If you feel confident enough to use it regardless, fine, but let's not make this the default. For example, we can add an optional_recursive_wrapper type. (I wouldn't suggest using a macro to trigger this behavior since it can lead to ODR violations). Solution 4 is probably worse than keeping variant as it is. Other than performance implications, adding throwability to a currently trivial function is something that should be avoided. Solution 3 is the only one that plays by the rules. It's not a universal solution, but it addresses enough cases and at least for now it's good enoguh. If you're put off by the intrusiveness of it - I understand. To paraphrase this solution in a more generic way: when possible, the conservative move-ctor of variant can destructively move the contained value and construct a cheap type to cover it up. If we had a generic way to do a destructive move, then variant wouldn't need to tinker with recursive_wrapper's internals and we could apply the same optimization for user types. I'd start with just recursive_wrapper though. I'd go with 3. -- Paul Smith

II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations + good performance + provides noexcept guarantee for move constructor + optimization will be used even if varinat has no type with trivial default constructor + easy to implement - triggers an assert when user tries to reuse moved object - adds an empty state to the recursive_wrapper
My vote goes to II. - Using moved-from objects should rarely happen, if ever. - It is not even a breaking change since move from variants was not supported in the past. Moreover, I am explicitly against proposition III: - The state of the moved-from variant is not explicitly set by the programmer. I tend to prefer explicit to implicit. - The type held in the moved-from variant depends on the types in the variant, which could make it harder to use variants in generic code. - It is simpler to have a single, well-documented behavior. It introduces less complexity. Question: Would it be possible to assign to moved-from variant? If so, proposition III is equivalent to proposition II with the following, unless I am mistaken: typedef variant<int, recursive_wrapper<foo> > V; V v1(std::move(v2)); // using v2 now triggers an assertion // (except when trying to set its content) v2 = int(); // this is explicit and equivalent to III Just my 2 cents. Louis Dionne

Louis Dionne wrote:
My vote goes to II.
- Using moved-from objects should rarely happen, if ever. - It is not even a breaking change since move from variants was not supported in the past.
That's not quite true. If a type doesn't have a move constructor, move is supported and same as copy.
Moreover, I am explicitly against proposition III:
- The state of the moved-from variant is not explicitly set by the programmer. I tend to prefer explicit to implicit. - The type held in the moved-from variant depends on the types in the variant, which could make it harder to use variants in generic code.
I fail to grasp the logic that simultaneously holds that
- Using moved-from objects should rarely happen, if ever.
and then proceeds to argue that moved-from objects should not be left in such-and-such valid state, but must be left in an invalid state instead.

Peter Dimov <lists <at> pdimov.com> writes:
Louis Dionne wrote:
My vote goes to II.
- Using moved-from objects should rarely happen, if ever. - It is not even a breaking change since move from variants was not supported in the past.
That's not quite true. If a type doesn't have a move constructor, move is supported and same as copy.
At first, I thought using proposition II would introduce a breaking change. After reading Joel de Guzman's post, I learned this was not the case. What I meant is that we don't run the risk of breaking existing code, which is one less reason of not adopting proposition II. Still, thanks for the precision.
[snip]
I fail to grasp the logic that simultaneously holds that
- Using moved-from objects should rarely happen, if ever.
and then proceeds to argue that moved-from objects should not be left in such-and-such valid state, but must be left in an invalid state instead.
In my post, I assume that the rare situations in which a moved-from variant is used are programming errors. In such case, I would definitely prefer the variant to be in an invalid state and to get an assertion ASAP so I can fix my code. With proposition III, using a moved-from variant implies that you know what's in it after the move, i.e. the type of the held object and the fact that it is default constructed. In such case, it is equivalent to create a default constructed object of that type and to use it instead. With proposition II, using a moved-from variant is just a no-no. You can create a default constructed instance of the type you want if you need to instead of relying on the implicit behavior of the moved-from variant. The above assumes that the variant types contain a recursive_wrapper. Best, Louis Dionne

Louis Dionne wrote:
With proposition III, using a moved-from variant implies that you know what's in it after the move, i.e. the type of the held object and the fact that it is default constructed.
No, [III] doesn't generally guarantee a specific value after a move. It just guarantees a valid object; that is, one that doesn't crash or assert if you try to use it. "Valid but unspecified" is the classic definition of a moved-from object.

On 01/21/13 07:39, Antony Polukhin wrote:
Current implementation of recursive_wrapper move constructor is not optimal:
recursive_wrapper<T>::recursive_wrapper(recursive_wrapper&& operand) : p_(new T(std::move(operand.get()) )) { }
During descussion were made following proposals: [snip] III: Make recursive_wrapper and variant cooperate, enable move for varinat in the presence of recursive_wrappers only when there is at least one type that is nothrow-default-constructible, regardless of whether it's current. It is easyer to understand by example: typedef variant<int, recursive_wrapper<foo>> V; V v1( std::move(v2) ); This move-constructs v1 from v2 and leaves int() into v2. + good performance + provides noexcept guarantee for rcursive_wrapper + does not adds an empty state - optimization won't trigger if varinat has no type with trivial default constructor - hard to implement - user may be obscured by the fact, that v2 from the example now contains int
[snip] What about III except the moved-from variant's which() method returns -2 (or some other "signal" value) signalling the object has been moved from? The moved-from variant's destructor and assign operators would check the which() value (as, I assume, they now do) and behave accordingly (the destructor would simply do nothing except recover the buffer memory). The user, using the example you provide above, would be, I believe, *less* obscured by this than v2 containing an int, because now v2 would contain some "signal" value, such as boost::blank which the user would realize meant something unusual had happened. Implementation, AFAICT, would be very simple and there would be no need to check if the variant had no type with trivial default constructor because boost::blank *does* have a trivial default constructor. The one downside I see is this might break existing code. Of course I'm probably missing something, and any enlightenment would be appreciated. -regards, Larry

Antony Polukhin wrote:
III: Make recursive_wrapper and variant cooperate, enable move for variant in the presence of recursive_wrappers only when there is at least one type that is nothrow-default-constructible, regardless of whether it's current.
That's not quite correct. Move for variant would be always enabled, it just won't result in a NULL reference_wrapper. For example, if a variant<int, string, reference_wrapper<string2>> contains int(5), it will continue to do so after move; and if it contains string("..."), it will hold string() after move. Only if it had a recursive_wrapper( string2("...") ) would it be int() after move, and if there was no suitable int in the list, it would either contain recursive_wrapper( string2("...") ), or recursive_wrapper( string2() ), depending on how the move constructor of recursive_wrapper is implemented. In addition, I'd argue that regardless of whether recursive_wrapper is made to be NULL after move, variant should still try to prevent exposing it to the user in the situations where there is an "int" in the list.
- user may be obscured by the fact, that v2 from the example now contains int
Users who are obscured by seeing an int() in v2 would be even more obscured by seeing a crash if v2 had a NULL recursive_wrapper instead.

Antony Polukhin wrote:
Current implementation of recursive_wrapper move constructor is not optimal:
recursive_wrapper<T>::recursive_wrapper(recursive_wrapper&& operand) : p_(new T(std::move(operand.get()) )) { }
During descussion were made following proposals:
<snip>
Please, vote for solution or propose a better one.
A very good post and interesting discussion. But one small sticking point in my opinion. I would prefer that the word "vote" be replaced with "make recommendation". The word "vote" suggests that all input will be weighted equally. Our traditional reviewing process recognises that all input is not equal and assigns to one person - review manager - the role of weighing all the input. In some cases the review manager may (and I assume has) over-ruled the majority because the arguments of the minority were better in his view. So I would have preferred that the the post used the phrase "Please submit recommendation on design" rather than "Please vote for behavior" Robert Ramey

On 21.01.2013 17:39, Antony Polukhin wrote:
I: Leave it as is - bad performance - can not provide noexcept guarantee
I vote against this solution because of it's disadvantages.
II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations + good performance + provides noexcept guarantee for move constructor + optimization will be used even if varinat has no type with trivial default constructor + easy to implement - triggers an assert when user tries to reuse moved object - adds an empty state to the recursive_wrapper Let's think about non-recursive variants. For example:
// both CFileObj and CDatabaseObj are movable, not copyable and // not nothrow-default-constructible boost::variant< CFileObj, CDatabaseObj > v1( CFileObj(SomeFileName) ); boost::variant< CFileObj, CDatabaseObj > v2( std::move( v1 ) ); // What would be the state of v1 at this point? It seems to me that the second solution solves only a part of the problem. So, I don't vote for it.
III: Make recursive_wrapper and variant cooperate, enable move for varinat in the presence of recursive_wrappers only when there is at least one type that is nothrow-default-constructible, regardless of whether it's current. It is easyer to understand by example: typedef variant<int, recursive_wrapper<foo>> V; V v1( std::move(v2) ); This move-constructs v1 from v2 and leaves int() into v2. + good performance + provides noexcept guarantee for rcursive_wrapper + does not adds an empty state - optimization won't trigger if varinat has no type with trivial default constructor - hard to implement - user may be obscured by the fact, that v2 from the example now contains int
I think this is a best solution. We have to leave the moved-from object in some valid state and cannot specify this state in the code. So, we need the default constructor (or, may be, something else).
IV: After move away, delay construction of type held by recursive_wrapper till it will be be required. +/- good performance? but more checks + provides noexcept guarantee for rcursive_wrapper + does not adds an explicit empty state + optimization will be used even if varinat has no type with trivial default constructor +/- not hard to implement --- additional requirement for type T of recursive_wrapper: it must be default constructible - less obvious behavior for user (for example get() function would construct values, allocate memory and can possibly throw) I think that get() MUST NOT throw. So, I vote against this solution.
-- Sergey Cheban

On January 21, 2013 11:27:36 PM Sergey Cheban <s.cheban@drweb.com> wrote:
II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations + good performance + provides noexcept guarantee for move constructor + optimization will be used even if varinat has no type with trivial default constructor + easy to implement - triggers an assert when user tries to reuse moved object - adds an empty state to the recursive_wrapper Let's think about non-recursive variants. For example:
// both CFileObj and CDatabaseObj are movable, not copyable and // not nothrow-default-constructible boost::variant< CFileObj, CDatabaseObj > v1( CFileObj(SomeFileName) ); boost::variant< CFileObj, CDatabaseObj > v2( std::move( v1 ) );
// What would be the state of v1 at this point?
Moved-from CFileObj, I presume.

2013/1/21 Andrey Semashev <andrey.semashev@gmail.com>:
On January 21, 2013 11:27:36 PM Sergey Cheban <s.cheban@drweb.com> wrote:
II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations + good performance + provides noexcept guarantee for move constructor + optimization will be used even if varinat has no type with trivial default constructor + easy to implement - triggers an assert when user tries to reuse moved object - adds an empty state to the recursive_wrapper Let's think about non-recursive variants. For example:
// both CFileObj and CDatabaseObj are movable, not copyable and // not nothrow-default-constructible boost::variant< CFileObj, CDatabaseObj > v1( CFileObj(SomeFileName) ); boost::variant< CFileObj, CDatabaseObj > v2( std::move( v1 ) );
// What would be the state of v1 at this point?
Moved-from CFileObj, I presume.
Correct, variant will contain CFileObj that was moved. I've got some nice idea from discussion: nullable variant. If many people use or want to use a variant with a type, that represents empty state, we can create a separate nullable variant class. I think that nullable variant can be implemented more efficient that the current variant. For example we can simplify copy/move constructors and assignment operators, guarantee fast noexcept default construction, simplify metaprogamming and reduce compilation times. Maybe someone want to implement it? -- Best regards, Antony Polukhin

On 1/22/13 3:57 AM, Antony Polukhin wrote:
I've got some nice idea from discussion: nullable variant. If many people use or want to use a variant with a type, that represents empty state, we can create a separate nullable variant class. I think that nullable variant can be implemented more efficient that the current variant. For example we can simplify copy/move constructors and assignment operators, guarantee fast noexcept default construction, simplify metaprogamming and reduce compilation times. Maybe someone want to implement it?
I like it! If you implement it, I'll be your first user :-) I don't really care much about this "never empty" guarantee and I think it's not really worth the trouble. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Mon, Jan 21, 2013 at 3:33 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/22/13 3:57 AM, Antony Polukhin wrote:
I've got some nice idea from discussion: nullable variant. If many people use or want to use a variant with a type, that represents empty state, we can create a separate nullable variant class. I think that nullable variant can be implemented more efficient that the current variant. For example we can simplify copy/move constructors and assignment operators, guarantee fast noexcept default construction, simplify metaprogamming and reduce compilation times. Maybe someone want to implement it?
I like it! If you implement it, I'll be your first user :-) I don't really care much about this "never empty" guarantee and I think it's not really worth the trouble.
How is this different from variant< blank, ... > ? - Jeff

On 1/22/13 7:49 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Jan 21, 2013 at 3:33 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/22/13 3:57 AM, Antony Polukhin wrote:
I've got some nice idea from discussion: nullable variant. If many people use or want to use a variant with a type, that represents empty state, we can create a separate nullable variant class. I think that nullable variant can be implemented more efficient that the current variant. For example we can simplify copy/move constructors and assignment operators, guarantee fast noexcept default construction, simplify metaprogamming and reduce compilation times. Maybe someone want to implement it?
I like it! If you implement it, I'll be your first user :-) I don't really care much about this "never empty" guarantee and I think it's not really worth the trouble.
How is this different from variant< blank, ... > ?
Less work behind the scenes. Lighter TMP load. better noexcept guarantees. Etc. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Mon, Jan 21, 2013 at 3:54 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/22/13 7:49 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Jan 21, 2013 at 3:33 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/22/13 3:57 AM, Antony Polukhin wrote:
I've got some nice idea from discussion: nullable variant. If many
people use or want to use a variant with a type, that represents empty state, we can create a separate nullable variant class. I think that nullable variant can be implemented more efficient that the current variant. For example we can simplify copy/move constructors and assignment operators, guarantee fast noexcept default construction, simplify metaprogamming and reduce compilation times. Maybe someone want to implement it?
I like it! If you implement it, I'll be your first user :-) I don't really care much about this "never empty" guarantee and I think it's not really worth the trouble.
How is this different from variant< blank, ... > ?
Less work behind the scenes.
You mean fewer LOC / simpler implementation (believable) or faster compiled code (I'd be skeptical)?
Lighter TMP load.
Agreed, but I'm not sure how much the "never empty guarantee" accounts for the present TMP load. better noexcept
guarantees. Etc.
I don't see this. I would think constructing blank has the same noexcept guarantees as assigning a pointer to null, and can equally well be propagated out to the variant interface. You might have a specific case in mind, though? Do you presently use variant< blank, ... >? If not, is it because of the above reasons? - Jeff

On 1/22/13 8:13 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Jan 21, 2013 at 3:54 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/22/13 7:49 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Jan 21, 2013 at 3:33 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/22/13 3:57 AM, Antony Polukhin wrote:
I've got some nice idea from discussion: nullable variant. If many
people use or want to use a variant with a type, that represents empty state, we can create a separate nullable variant class. I think that nullable variant can be implemented more efficient that the current variant. For example we can simplify copy/move constructors and assignment operators, guarantee fast noexcept default construction, simplify metaprogamming and reduce compilation times. Maybe someone want to implement it?
I like it! If you implement it, I'll be your first user :-) I don't really care much about this "never empty" guarantee and I think it's not really worth the trouble.
How is this different from variant< blank, ... > ?
Less work behind the scenes.
You mean fewer LOC / simpler implementation (believable) or faster compiled code (I'd be skeptical)?
Agreed.
Lighter TMP load.
Agreed, but I'm not sure how much the "never empty guarantee" accounts for the present TMP load.
Agreed.
better noexcept
guarantees. Etc.
I don't see this. I would think constructing blank has the same noexcept guarantees as assigning a pointer to null, and can equally well be propagated out to the variant interface. You might have a specific case in mind, though?
No. After thinking about it for a while, you are right.
Do you presently use variant< blank, ... >? If not, is it because of the above reasons?
I *always* use variant< blank, ... > (or something similar). And that is precisely why I don't care much about the never-empty guarantee. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Tue, Jan 22, 2013 at 2:25 AM, Joel de Guzman <djowel@gmail.com> wrote:
I *always* use variant< blank, ... > (or something similar). And that is precisely why I don't care much about the never-empty guarantee.
I hope you *always* use variant<blank, ...> because it makes sense in the cases you use it, and not just because that's the way the cookie crumbles. I also hope you appreciate that not everybody vacuously uses variant like that, nor should they. Anyway, you're lucky then. Peter's solution will give you as much benefit as a nulled recursive_wrapper. Where's the problem? -- Paul Smith

On 1/28/13 10:17 PM, Paul Smith wrote:
On Tue, Jan 22, 2013 at 2:25 AM, Joel de Guzman <djowel@gmail.com> wrote:
I *always* use variant< blank, ... > (or something similar). And that is precisely why I don't care much about the never-empty guarantee.
I hope you *always* use variant<blank, ...> because it makes sense in the cases you use it, and not just because that's the way the cookie crumbles. I also hope you appreciate that not everybody vacuously uses variant like that, nor should they.
Anyway, you're lucky then. Peter's solution will give you as much benefit as a nulled recursive_wrapper. Where's the problem?
The problem is that it is a workaround for something that I no longer think as necessary. It is a workaround that goes around the problem that *all* proxy-like classes will have given your view of how move should behave. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On Mon, Jan 28, 2013 at 6:18 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/28/13 10:17 PM, Paul Smith wrote:
On Tue, Jan 22, 2013 at 2:25 AM, Joel de Guzman <djowel@gmail.com> wrote:
I *always* use variant< blank, ... > (or something similar). And that is precisely why I don't care much about the never-empty guarantee.
I hope you *always* use variant<blank, ...> because it makes sense in the cases you use it, and not just because that's the way the cookie crumbles. I also hope you appreciate that not everybody vacuously uses variant like that, nor should they.
Anyway, you're lucky then. Peter's solution will give you as much benefit as a nulled recursive_wrapper. Where's the problem?
The problem is that it is a workaround for something that I no longer think as necessary. It is a workaround that goes around the problem that *all* proxy-like classes will have given your view of how move should behave.
Yup, that's right. Except that it's not really my view, and I definitely don't want to be holding the flag for conservative move semantics. I'm just describing how things are (as far as I understand them). -- Paul Smith

On 01/21/13 18:25, Joel de Guzman wrote:
On 1/22/13 8:13 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Jan 21, 2013 at 3:54 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/22/13 7:49 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Jan 21, 2013 at 3:33 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/22/13 3:57 AM, Antony Polukhin wrote:
I've got some nice idea from discussion: nullable variant. If many
people use or want to use a variant with a type, that represents empty state, we can create a separate nullable variant class. I think that nullable variant can be implemented more efficient that the current variant. For example we can simplify copy/move constructors and assignment operators, guarantee fast noexcept default construction, simplify metaprogamming and reduce compilation times. Maybe someone want to implement it?
I like it! If you implement it, I'll be your first user :-) I don't really care much about this "never empty" guarantee and I think it's not really worth the trouble.
How is this different from variant< blank, ... > ?
Less work behind the scenes.
You mean fewer LOC / simpler implementation (believable) or faster compiled code (I'd be skeptical)?
Agreed.
Lighter TMP load.
Agreed, but I'm not sure how much the "never empty guarantee" accounts for the present TMP load.
Agreed.
better noexcept
guarantees. Etc.
I don't see this. I would think constructing blank has the same noexcept guarantees as assigning a pointer to null, and can equally well be propagated out to the variant interface. You might have a specific case in mind, though?
No. After thinking about it for a while, you are right.
Do you presently use variant< blank, ... >? If not, is it because of the above reasons?
I *always* use variant< blank, ... > (or something similar). And that is precisely why I don't care much about the never-empty guarantee.
Regards,
I'm a bit confused about what nullable variant means. I can see 2 possible interpretations: 1) allow recursive_wrapper<T>::_p == 0. This only occurs when moving from a variant containing a recursive_wrapper. 2) ensure a moved from variant is put into the same state as a default constructed variant. Am I missing something? -regards, Larry

On 1/29/13 12:47 AM, Larry Evans wrote:
On 01/21/13 18:25, Joel de Guzman wrote:
On 1/22/13 8:13 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Jan 21, 2013 at 3:54 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/22/13 7:49 AM, Jeffrey Lee Hellrung, Jr. wrote:
On Mon, Jan 21, 2013 at 3:33 PM, Joel de Guzman <djowel@gmail.com> wrote:
On 1/22/13 3:57 AM, Antony Polukhin wrote:
I've got some nice idea from discussion: nullable variant. If many > people use or want to use a variant with a type, that represents > empty > state, we can create a separate nullable variant class. I think that > nullable variant can be implemented more efficient that the current > variant. For example we can simplify copy/move constructors and > assignment operators, guarantee fast noexcept default construction, > simplify metaprogamming and reduce compilation times. Maybe someone > want to implement it? > > I like it! If you implement it, I'll be your first user :-) I don't really care much about this "never empty" guarantee and I think it's not really worth the trouble.
How is this different from variant< blank, ... > ?
Less work behind the scenes.
You mean fewer LOC / simpler implementation (believable) or faster compiled code (I'd be skeptical)?
Agreed.
Lighter TMP load.
Agreed, but I'm not sure how much the "never empty guarantee" accounts for the present TMP load.
Agreed.
better noexcept
guarantees. Etc.
I don't see this. I would think constructing blank has the same noexcept guarantees as assigning a pointer to null, and can equally well be propagated out to the variant interface. You might have a specific case in mind, though?
No. After thinking about it for a while, you are right.
Do you presently use variant< blank, ... >? If not, is it because of the above reasons?
I *always* use variant< blank, ... > (or something similar). And that is precisely why I don't care much about the never-empty guarantee.
Regards,
I'm a bit confused about what nullable variant means. I can see 2 possible interpretations:
1) allow recursive_wrapper<T>::_p == 0. This only occurs when moving from a variant containing a recursive_wrapper. 2) ensure a moved from variant is put into the same state as a default constructed variant.
Closer to 2. A nullable_variant is simply a variant with an implied "blank" type as the first in the type list. It's equivalent to variant<blank, T...> I can imagine that the implementation of this will be cleaner and simpler than the current implementation with lots of code that deals with the never-empty guarantee. Regards, -- Joel de Guzman http://www.boostpro.com http://boost-spirit.com

On 1/22/13 3:57 AM, Antony Polukhin wrote:
I've got some nice idea from discussion: nullable variant. If many people use or want to use a variant with a type, that represents empty state, we can create a separate nullable variant class. I think that nullable variant can be implemented more efficient that the current variant. For example we can simplify copy/move constructors and assignment operators, guarantee fast noexcept default construction, simplify metaprogamming and reduce compilation times. Maybe someone want to implement it?
I like it! If you implement it, I'll be your first user :-) I don't really care much about this "never empty" guarantee and I think it's not really worth the trouble.
I'd prefer to have a nullable variant as well. If not that, then I would like to support II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations. Regards Hartmut --------------- http://boost-spirit.com http://stellar.cct.lsu.edu

On 01/21/13 17:51, Hartmut Kaiser wrote:
On 1/22/13 3:57 AM, Antony Polukhin wrote:
I've got some nice idea from discussion: nullable variant. If many people use or want to use a variant with a type, that represents empty state, we can create a separate nullable variant class. I think that nullable variant can be implemented more efficient that the current variant. For example we can simplify copy/move constructors and assignment operators, guarantee fast noexcept default construction, simplify metaprogamming and reduce compilation times. Maybe someone want to implement it?
I like it! If you implement it, I'll be your first user :-) I don't really care much about this "never empty" guarantee and I think it's not really worth the trouble.
I'd prefer to have a nullable variant as well. If not that, then I would like to support II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations.
OR have get<T> return recursive_wrapper<T> and then the user would have to check whether it's nulled. This avoids the need for BOOST_ASSERT; however it does violate the contract: This "never-empty" property insulates the user from the possibility of undefined variant content and the significant additional complexity-of-use attendant with such a possibility. mentioned here: http://www.boost.org/doc/libs/1_52_0/doc/html/variant/design.html#variant.de... however, setting operator.p_ to NULL does the same but makes it harder for the user to detect since the user would have to use a try/catch to test it, IIUC. -regards, Larry

On 01/24/13 13:56, Larry Evans wrote:
On 01/21/13 17:51, Hartmut Kaiser wrote:
On 1/22/13 3:57 AM, Antony Polukhin wrote:
I've got some nice idea from discussion: nullable variant. If many people use or want to use a variant with a type, that represents empty state, we can create a separate nullable variant class. I think that nullable variant can be implemented more efficient that the current variant. For example we can simplify copy/move constructors and assignment operators, guarantee fast noexcept default construction, simplify metaprogamming and reduce compilation times. Maybe someone want to implement it?
I like it! If you implement it, I'll be your first user :-) I don't really care much about this "never empty" guarantee and I think it's not really worth the trouble.
I'd prefer to have a nullable variant as well. If not that, then I would like to support II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations.
OR have get<T> return recursive_wrapper<T> and then the user would have to check whether it's nulled. This avoids the need for BOOST_ASSERT; however it does violate the contract:
This "never-empty" property insulates the user from the possibility of undefined variant content and the significant additional complexity-of-use attendant with such a possibility.
mentioned here:
http://www.boost.org/doc/libs/1_52_0/doc/html/variant/design.html#variant.de...
however, setting operator.p_ to NULL does the same but makes it harder for the user to detect since the user would have to use a try/catch to test it, IIUC.
-regards, Larry
However, if you're allowing recursive_wrapper<T>::get_pointer() to return 0, why not just put the recursive_wrapper closer to where the recursion actually occurs? After all, a null pointer is one way to signal the end of a recursive data structure. IOW, something like the first attachment. The current way of doing the same is the 2nd attachment. This current way puts recursive_wrapper as one of the variant's bounded types; however, the 1st attachment shows it used as a member of one of the bounded types. That seems more like the way its normally done. After all, the domain equation for the simplest recursive type is: List<T> = Null + T*List<T> which is closer to what the 1st attachment has, except, instead of T*List<T> it has: fusion::vector<T,recursive_wrapper<List<T> > and instead lf Null it has: boost::blank. In contrast, the 2nd attachment is more like: List<T> = Null + recursive_wrapper<T*List<T> > The Null + T*recursive_wrapper<List<T> > seems closer to how the actual recursion is implemented. -regards, Larry

On 01/24/13 16:16, Larry Evans wrote:
On 01/24/13 13:56, Larry Evans wrote:
On 01/21/13 17:51, Hartmut Kaiser wrote:
On 1/22/13 3:57 AM, Antony Polukhin wrote:
I've got some nice idea from discussion: nullable variant. If many people use or want to use a variant with a type, that represents empty state, we can create a separate nullable variant class. I think that nullable variant can be implemented more efficient that the current variant. For example we can simplify copy/move constructors and assignment operators, guarantee fast noexcept default construction, simplify metaprogamming and reduce compilation times. Maybe someone want to implement it?
I like it! If you implement it, I'll be your first user :-) I don't really care much about this "never empty" guarantee and I think it's not really worth the trouble.
I'd prefer to have a nullable variant as well. If not that, then I would like to support II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations.
OR have get<T> return recursive_wrapper<T> and then the user would have to check whether it's nulled. This avoids the need for BOOST_ASSERT; however it does violate the contract:
This "never-empty" property insulates the user from the possibility of undefined variant content and the significant additional complexity-of-use attendant with such a possibility.
mentioned here:
http://www.boost.org/doc/libs/1_52_0/doc/html/variant/design.html#variant.de...
however, setting operator.p_ to NULL does the same but makes it harder for the user to detect since the user would have to use a try/catch to test it, IIUC.
-regards, Larry
However, if you're allowing recursive_wrapper<T>::get_pointer() to return 0, why not just put the recursive_wrapper closer to where the recursion actually occurs? After all, a null pointer is one way to signal the end of a recursive data structure.
IOW, something like the first attachment.
The current way of doing the same is the 2nd attachment. This current way puts recursive_wrapper as one of the variant's bounded types; however, the 1st attachment shows it used as a member of one of the bounded types. That seems more like the way its normally done. After all, the domain equation for the simplest recursive type is:
List<T> = Null + T*List<T>
which is closer to what the 1st attachment has, except, instead of T*List<T> it has:
fusion::vector<T,recursive_wrapper<List<T> >
and instead lf Null it has:
boost::blank.
In contrast, the 2nd attachment is more like:
List<T> = Null + recursive_wrapper<T*List<T> >
The Null + T*recursive_wrapper<List<T> > seems closer to how the actual recursion is implemented.
-regards, Larry
OOPS, allowing recursive_wrapper<T>::get_pointer() == 0 violates the domain equation: List<T> = Null + T*List<T> because with a null recursive_wrapper, there are 3 possible states: List<T> = Null + T*List<T> + List<T>*==0 where List<T>*==0 means a null pointer to a List<T>. The only way I can see to solve that problem is to disallow recursive_wrapper<T>::get_pinter() == 0 and, when moving from a List<T>, set the moved from state to Null, AFAICT. -Larry

21.01.2013 23:37, Andrey Semashev пишет:
On January 21, 2013 11:27:36 PM Sergey Cheban <s.cheban@drweb.com> wrote:
II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations + good performance + provides noexcept guarantee for move constructor + optimization will be used even if varinat has no type with trivial default constructor + easy to implement - triggers an assert when user tries to reuse moved object - adds an empty state to the recursive_wrapper Let's think about non-recursive variants. For example:
// both CFileObj and CDatabaseObj are movable, not copyable and // not nothrow-default-constructible boost::variant< CFileObj, CDatabaseObj > v1( CFileObj(SomeFileName) ); boost::variant< CFileObj, CDatabaseObj > v2( std::move( v1 ) );
// What would be the state of v1 at this point?
Moved-from CFileObj, I presume. Got it. So, the reasonable and consistent value for the moved-from variant<recursive_wrapper,int> is a moved-from recursive_wrapper, right? In many cases, the moved-from state of the variables is equal to the default-constructed state but this rule is not obligatory.
So, I think that NULL is a valid state of the moved-from recursive_wrapper and the 2nd proposal is not worse (at least) than the 3rd one. -- Best regards, Sergey Cheban

On Mon, Jan 21, 2013 at 5:39 AM, Antony Polukhin <antoshkka@gmail.com>wrote:
Current implementation of recursive_wrapper move constructor is not optimal:
recursive_wrapper<T>::recursive_wrapper(recursive_wrapper&& operand) : p_(new T(std::move(operand.get()) )) { }
During descussion were made following proposals:
I: Leave it as is
[...]
II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations
[...]
III: Make recursive_wrapper and variant cooperate, enable move for varinat in the presence of recursive_wrappers only when there is at least one type that is nothrow-default-constructible, regardless of whether it's current.
[...]
IV: After move away, delay construction of type held by recursive_wrapper till it will be be required.
[...]
Please, vote for solution or propose a better one.
IMHO, III is the only strictly correct solution, given variant's present never-empty guarantee (which the original author deemed pretty important, given the amount of documentation dedicated to it). Aside: recursive_wrapper is only used within the context of a variant, right? - Jeff

On Mon, Jan 21, 2013 at 4:17 PM, Jeffrey Lee Hellrung, Jr. < jeffrey.hellrung@gmail.com> wrote:
IMHO, III is the only strictly correct solution, given variant's present never-empty guarantee (which the original author deemed pretty important, given the amount of documentation dedicated to it).
Unfortunately, I agree this is the only correct solution. I think all the other 'votes' are voting for "I wish move worked differently than what has been laid out in the standard". Tony

On 1/21/2013 8:39 AM, Antony Polukhin wrote:
Current implementation of recursive_wrapper move constructor is not optimal:
recursive_wrapper<T>::recursive_wrapper(recursive_wrapper&& operand) : p_(new T(std::move(operand.get()) )) { }
During descussion were made following proposals:
I: Leave it as is - bad performance - can not provide noexcept guarantee
II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations + good performance + provides noexcept guarantee for move constructor + optimization will be used even if varinat has no type with trivial default constructor + easy to implement - triggers an assert when user tries to reuse moved object - adds an empty state to the recursive_wrapper
III: Make recursive_wrapper and variant cooperate, enable move for varinat in the presence of recursive_wrappers only when there is at least one type that is nothrow-default-constructible, regardless of whether it's current. It is easyer to understand by example: typedef variant<int, recursive_wrapper<foo>> V; V v1( std::move(v2) ); This move-constructs v1 from v2 and leaves int() into v2. + good performance + provides noexcept guarantee for rcursive_wrapper + does not adds an empty state - optimization won't trigger if varinat has no type with trivial default constructor - hard to implement - user may be obscured by the fact, that v2 from the example now contains int
IV: After move away, delay construction of type held by recursive_wrapper till it will be be required. +/- good performance? but more checks + provides noexcept guarantee for rcursive_wrapper + does not adds an explicit empty state + optimization will be used even if varinat has no type with trivial default constructor +/- not hard to implement --- additional requirement for type T of recursive_wrapper: it must be default constructible - less obvious behavior for user (for example get() function would construct values, allocate memory and can possibly throw)
The main issue seems to be simply this: are guarantees ( invariants ) for an object of a class meant to cover moved from objects of that class ? All of the choices which you have specified, which popularly boils down to II or III, involves this question. The choice of II answers No to the question above while the choice of III answers Yes to the question above. But the issue is larger than this particular case and really needs comment from someone who understands the C++ standard, rvalue references, and moved objects beyond what is written in the C++ standard document. For instance I read in Lippman's "C++ Primer 5th Edition" that the only things one should be able to do with a moved from object is destroy or assign new values to it. This implies to me that a moved from object should not really be used in any way unless one can repopulate its values. IMO this supports the choice of II and that a guarantee for a moved from object should no longer hold, since it is hopeless to use that object unless one assigns something to it. But perhaps Lippman's POV is not really that of the C++ standard committee regarding the usefulness of moved from objects. Or perhaps, despite the fact that the moved from object should not be used unless an appropriate value(s) is reassigned to it, guarantees on a moved from object should still hold. I really do not know, but it is important to clarify what the disagreement is about and that it goes well beyond the present case.

On 01/21/13 22:02, Edward Diener wrote:
On 1/21/2013 8:39 AM, Antony Polukhin wrote:
Current implementation of recursive_wrapper move constructor is not optimal:
recursive_wrapper<T>::recursive_wrapper(recursive_wrapper&& operand) : p_(new T(std::move(operand.get()) )) { }
During descussion were made following proposals: [snip] II: Set operand.p_ to NULL, add BOOST_ASSERT in get operations + good performance + provides noexcept guarantee for move constructor + optimization will be used even if varinat has no type with trivial default constructor + easy to implement - triggers an assert when user tries to reuse moved object - adds an empty state to the recursive_wrapper
III: Make recursive_wrapper and variant cooperate, enable move for varinat in the presence of recursive_wrappers only when there is at least one type that is nothrow-default-constructible, regardless of whether it's current. It is easyer to understand by example: typedef variant<int, recursive_wrapper<foo>> V; V v1( std::move(v2) ); This move-constructs v1 from v2 and leaves int() into v2. + good performance + provides noexcept guarantee for rcursive_wrapper + does not adds an empty state - optimization won't trigger if varinat has no type with trivial default constructor - hard to implement - user may be obscured by the fact, that v2 from the example now contains int [snip] The main issue seems to be simply this: are guarantees ( invariants ) for an object of a class meant to cover moved from objects of that class ?
All of the choices which you have specified, which popularly boils down to II or III, involves this question. The choice of II answers No to the question above while the choice of III answers Yes to the question above.
The attached code is about the simplest recursive wrapper possible. The output, also attached, illustrates the current behaviour. AFAICT, the proposed changes, II and III would cause the following changes in the output. I think proposal II would cause the: 113:list_1 moved which=1 line to remain unchanged; however, the next line: 114:list_1 moved print=( 1, (blank) ) would not complete because, since the recursive_wrapper pointer would be null, the const list_bounded<T>& value needed for: list_printer::operator()(const list_bounded<T>& lst) const could not be retrieved because the assertion:
- triggers an assert when user tries to reuse moved object
would be triggered first. In contrast, proposal III would cause the line 113 output to be: 113:list_1 moved which=1 and the line 114 output to be: 114:list_1 moved print=(blank) and the rest of the output the same. Is that about right? -regards, Larry
participants (23)
-
Andrey Semashev
-
Antony Polukhin
-
Ben Pope
-
Daniel James
-
Dave Abrahams
-
Edward Diener
-
Emil Dotchevski
-
Giovanni Piero Deretta
-
Gottlob Frege
-
Hartmut Kaiser
-
Jeffrey Lee Hellrung, Jr.
-
Joel de Guzman
-
Krzysztof Czainski
-
Larry Evans
-
Louis Dionne
-
Michael Marcin
-
Neil Groves
-
Paul Smith
-
Peter Dimov
-
Rob Stewart
-
Robert Ramey
-
Sebastian Redl
-
Sergey Cheban