|
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