Boost logo

Boost :

From: David Abrahams (dave_at_[hidden])
Date: 2003-01-31 11:53:18


"David B. Held" <dheld_at_[hidden]> writes:

>> smart_ptr(smart_ptr & rhs)
>> : storage(rhs), ownership(rhs), checking(rhs), conversion(rhs)
>> {}
>>
>> Its purpose is to take ownership away from the auto_ptr-like rhs
>> (you wanted to support that) and construct a counted_ptr.
>
> Hmm...I'm not so sure about constructing a ref-counted pointer from
> a move pointer. That's an interesting idea that I hadn't really
> considered. I'm not sure whether it's a good idea to allow that or
> not. It might encourage indiscriminate mixing of pointer types.

If you want to subsume the current shared_ptr and auto_ptr you'd
better handle it.

There are other ways to prevent indiscriminate mixing (an orthogonal
"interoperability policy", perhaps?)

>> As storage is constructed first, it needs to move from rhs.storage;
>> now when ownership throws, rhs.storage will be left empty, and the
>> object is gone. This is not that big of a problem... provided that the
>> other rhs policies can handle storage being zeroed.
>
> They'd better be able to handle this, since is this is what would
> happen in the *non-exceptional* case as well. However, the other
> policies had better not be able to tell whether or not storage is zeroed,
> or we have lost orthogonality.
>
> A more interesting related problem involves what to do when
> transferring a managed resource. When tranferring an unmanaged
> resource, you have to clean it up on an exception, just to get the
> basic guarantee.

Not quite. You have to clean it up on exception in order to be
usable, or for certain expressions outside the constructor itself to
give the basic guarantee, e.g.:

     smart_ptr<T> x(new T);

If you leak the T in that case, the constructor still gives the basic
guarantee. Allow me to demonstrate:

     std::auto_ptr<T> x(new T);
     smart_ptr<T> y(x.get());
     x.release();

smart_ptr<T>'s constructor gives the basic (and strong) guarantee in
both cases. In principle you don't have to promise to delete the
unmanaged resource if your constructor throws; it's just your desire
to be usable with "new T" that makes it neccessary.

In fact, I have been arguing for years that our smart pointers should
never have had a public interface which adopts unmanaged resources on
construction. Instead, we should write:

     std::auto_ptr<T> = std::auto_ptr_new<T>(arg1, arg2, arg3);

Voila, a managed T straight out of the box.

However, the desire to subsume existing smart pointers means you have
to support the unsafe "unmanaged adoption interface."

> However, when moving from a managed pointer, cleaning up will only
> *get* you the basic guarantee. To get the strong guarantee, you
> need to *move the resource back*. This is a tricky situation that
> might be a good argument to not support move semantics. ;) Or maybe
> the strong guarantee isn't important for this. I dunno. Comments?

The move proposal says that standard components will require the move
construct/assign of their UDT parameters, if any, to be nothrow. In
practice that would be needed by any generic components. That's
enough to make the move undo-able.

-- 
                       David Abrahams
   dave_at_[hidden] * http://www.boost-consulting.com
Boost support, enhancements, training, and commercial distribution

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