Boost logo

Boost :

Subject: Re: [boost] [variant] Please vote for behavior
From: Antony Polukhin (antoshkka_at_[hidden])
Date: 2013-01-31 02:24:21


2013/1/30 Paul Smith <pl.smith.mail_at_[hidden]>:
> On Wed, Jan 30, 2013 at 12:58 PM, Antony Polukhin <antoshkka_at_[hidden]> wrote:
>> I think that this example is perfect for showing the benefits of
>> nulled recursive wrapper.
>> Watch the hands:
>>
>> union my_union {
>> my_union u_; // Impossible to do
>> int i_;
>> };
>>
>> Because we can not use field of type my_union in my_union we need to wrap it:
>>
>> union my_union {
>> recursive_wrapper<my_union> u_; // Ok
>> int i_;
>> };
>>
>> Now we just need to decide, what a recursive_wrapper is! Is it behave
>> like a reference:
>>
>> // # 1
>> union my_union {
>> my_union& u_;
>> int i_;
>> };
>>
>> Or is it behave like a pointer:
>>
>> // # 2
>> union my_union {
>> unique_ptr<my_union> u_;
>> int i_;
>> };
>>
>> #1 is not what a recursive_wrapper is, because recursive_wrapper OWNS a value
>> #2 is much closer to functionality of recursive_wrapper
>>
>
> Neither of these are models of what recursive_wrapper is. It's not a
> pointer and it's not a reference. A unique_ptr owns an object, not a
> value. It can own different objects or nothing at all during its
> lifetime. A reference is an alias for an object. A recursive_wrapper
> *is* the object it wraps and its value *is* the object's value - it
> doesn't own anything else or have any other visible state. The fact
> that it has its storage allocated on the heap is an implementation
> detail. The point is simply to allow recursive_wrapper<T> to be a
> complete type even when T isn't.
>
> A variant that contains a recursive_wrapper<T> behaves exactly like a
> variant that contains a T, except that T can be a recursive type -
> that's all. And this is exactly why it shouldn't have different
> semantics - move or otherwise. If you want the semantics offered by
> unique_ptr, shared_ptr, "copy_ptr", reference_wrapper, optional or
> anything else - by all means, use that!

>From theoretical point of view you are absolutely correct, and my
example is lame. Moreover, current implementation of move assignment
and move constructors for recursive_wrapper were implemented to model
that behavior.

But I do understand Joel's efforts to make it faster. Just look at the
variants assign implementation for type T and imagine that T is a
recursive_wrapper:

void move_assign(T&& rhs) {
...
// heap allocation in move constructor of recursive_wrapper
variant temp( detail::variant::move(rhs) );

// heap allocation in move constructor of recursive_wrapper
// Potential call to less effective move_assign implementation if
// variant does not have fallback_type (+1 heap allocation and deallocation)
variant_assign( detail::variant::move(temp) );
...
// heap deallocation in destructor of recursive_wrapper
}

Allowing destructive move constructor for recursive_wrapper will make
variant at least 3 times faster.
Leaving recursive_wrapper as is will provoke users to use a really
slow implementation. Unique_ptr and other types can not be used as a
drop in replacement for recursive_wrapper.

May be we shall deprecate recursive_wrapper and add
recursive_ptr/nullable_recursive_wrapper class, that implements
destructive move constructor? It does not break recursive_wrapper
invariants,but adds a class with different invariants.

--
Best regards,
Antony Polukhin

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