Boost logo

Boost :

From: Howard Hinnant (hinnant_at_[hidden])
Date: 2004-01-21 11:52:50


On Jan 21, 2004, at 10:01 AM, Daniel Wallin wrote:

> Please present _real_ common use cases where this is needed,
> and where move_ptr can't be replaced with shared_ptr.

Ok, this isn't common, but I'm going to share a little bit of the
Metrowerks std::tr1::shared_ptr implementation as it demonstrates one
aspect of move_ptr<T, D> that I find very attractive:

template<class T>
template<class Y, class D>
shared_ptr<T>::shared_ptr(Y* p, D d)
        : ptr_(p)
{
        Metrowerks::move_ptr<Y, D&> hold(p, d);
        s_ = new detail::shared_ptr_deleter<Y, D>(p, d);
        hold.release();
        ...
}

This is the shared_ptr constructor that takes a pointer and a deleter.
It accepts ownership of the pointer no matter what ... even if the
construction fails with a bad_alloc. In order to accomplish this
exception safety I create a temporary move_ptr<T, D&> to establish
ownership in a no-throw manner. Note the "D&"!

There are several elegant things happening by being able to specify not
only this custom deleter for move_ptr, but make the move_ptr's deleter
a /reference/ to the deleter.

I know very little about D. Does it's copy constructor throw? Is it
expensive to copy construct? By storing a reference to D in the
move_ptr instead of a copy of D, I don't care. I know that the
move_ptr ctor won't throw, and thus I know that I've safely secured
ownership of p.

In other contexts (no longer talking about shared_ptr now) one can use
a reference to the deletion policy in order to protect yourself from
loosing information from a stateful deleter.

template <class T, class D>
foo::bar(T* p)
{
    // claim ownership of p
    move_ptr<T, D&> hold(p, d_);
    ...
    // transfer ownership of p to foo
    hold.release();
}

What if foo.d_ is a stateful deleter collecting statistics? If hold
has to do anything with p and d_ before it transfers ownership to foo
(such as d_(p)), then by having hold take a D& instead of a copy of D,
the state is guaranteed to be maintained.

shared_ptr isn't appropriate in the foo::bar context because it is
overly expensive, and introduces a needless possibility of a throw
(though that throw would not necessarily compromise exception safety
because shared_ptr used move_ptr to aggressively claim ownership of p!
:-) ).

After all of this about D&, note that you don't always want your
deleter to be a reference type. For one thing, if D is stateless then
the deletion policy overhead can be optimized away for move_ptr<T, D>,
but not for move_ptr<T, D&>. Ultimately containers of move_ptr<T, D>
are going to be possible, so overhead concerns are very important.
Thus I very much value having this kind of choice in move_ptr.

-Howard


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