Boost logo

Boost :

Subject: Re: [boost] [variant] Basic rvalue and C++11 features support
From: Joel de Guzman (djowel_at_[hidden])
Date: 2013-01-08 06:18:09

On 1/8/13 11:05 AM, Paul Smith wrote:
> On Tue, Jan 8, 2013 at 2:04 AM, Joel de Guzman <djowel_at_[hidden]> wrote:
>> On 1/8/13 4:14 AM, Paul Smith wrote:
>>> A recursive_wrapper is not a pointer. It's a value-like wrapper
>>> that is assumed to always contain a valid object. The move
>>> constructor should leave the moved-from recursive_wrapper
>>> in a valid state, which precludes nullifying it.
>>> That is, unless you suggest adding an "empty" state to
>>> recursive_wrapper, which doesn't sound like a very good
>>> idea.
>> I disagree. That state will happen only when copying rvalues which
>> will immediately be destructed anyway. What danger do you see in
>> that situation? Example:
>> recursive_wrapper<foo> bar() {...} // function returning
>> recursive_wrapper
>> recursive_wrapper<foo> foo(bar()); // copy
>> Under no circumstances will anyone get to see that "empty" state.
>> Do you see something that I don't?
>> Without this move optimization (as it currently is), it is very inefficient
>> especially with big structures (e.g. tuples and fusion adapted structs).
>> Without this optimization, such temporary copies will end up with two
>> heap allocations and unnecessary copying of the structures, instead of
>> one heap allocation and a simple pointer swap. That would mean the missed
>> optimization in the order of magnitudes with applications that use variant
>> heavily (e.g. Spirit).
> I hear ya. However, the C++11 take on move semantics is, for better
> and for worse, a conservative one and not a destructive one. It's a
> common misconception that a moved-from object is "as good as dead" and
> that the move constructor can perform an autopsy. (It took me a while
> to realize that, too).
> A move constructor has merely a "license to mutate" the source object
> while keeping it's invariants, not a "license to kill". This is, I
> believe, the intended convention. This is more or less what the STL
> expect from client types. And, even though it's not enforced by the
> core language, the effective semantics of rvalues and
> rvalue-references are such that a destructive move is unsafe. In
> particular, subobjects of rvalues are rvalues of the same value
> category, and can equally well bind to rv-refs. Even if a top-level
> rvalue is immediately destroyed after one of its subojects has been
> moved-from, its destructor might still attempt to use that subobject.
> If a type gives me public access to one of it's subobjects, it means
> that I'm allowed to mutate it using it's public interface any way I
> want, but nothing more. If the subobject's move consutrctor (which I
> can rightfully invoke) does something that's impossible to acheive
> using the public interface, I'm breaking this contract, and even if I
> no longer use the containing object, I might get punished when it's
> destructor takes place:
> class base {
> void something_naughty(); // don't attempt to use the
> // object after calling me
> public:
> base();
> base(base&& other) {other.something_naughty();}
> void something_ordinary();
> };
> struct derived : base {
> ~derived() {something_ordinary();}
> };
> int main() {
> base b = derived(); // I'm only using the public interface
> // of derived. How did I manage to
> // screw something up?
> }
> Note that this also applies to data members.
> Also, note that rvalues can ofcourse be std::moved lvalues, in which
> case they can be accessed by the programmer after having moved them,
> and he might wish to reuse them (or, if the lvalue is an lv-ref
> parameter, he might not know if whoever passed him this lvalue does),
> even though, admittedly, I haven't seen many convincing examples of
> such cases.
> I agree that the cases for which a destructive move is appropriate
> seem by far the dominant ones (even if it's no different than a
> conservative move in many of them). If you're asking for a rationale
> as to why the semantics are the way they are, then you're talking to
> the wrong person. I am sure David Abrahams can give a much more
> credible answer than I can. Some of these issues are discussed in
> N1377, in particular, see the "Alternative Move Designs" ->
> "Destructive move semantics" section
> (
> move designs)
> (Also note that the "Copy vs Move" section literally states: "The only
> requirement (from the move-ctor PS) is that the (source PS) object
> remain in a self consistent state (all internal invariants are still
> intact).")
> FWIW, I'm playing with the idea of writing a destructive-move proposal
> (although what I'm reffering to as destructive-move is a bit different
> than what the move proposal calls a destructive-move), and these kind
> of cases are exactly what I wish to address. If you have actual
> numbers that demonstrate how bad is the impact on Spirirt and co, I'd
> love to hear them.

Those are very good points, but I think you are being too pedantic.
The example you posted concerns a class hierarchy (base / derived).
The paper you cited mentions:

   "When dealing with class hierarchies, destructive move semantics
   becomes problematic."

That is certainly not the case with recursive_wrapper. Do you see
a reasonable cause for danger with (a non-hierarchical struct such
as) recursive_wrapper?

Finally, it would be best if you quote from the standard. That paper
you posted is quite old (from 2002).


Joel de Guzman

Boost list run by bdawes at, gregod at, cpdaniel at, john at