
2013/1/30 Paul Smith <pl.smith.mail@gmail.com>:
On Wed, Jan 30, 2013 at 12:58 PM, Antony Polukhin <antoshkka@gmail.com> 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