From: Howard Hinnant (hinnant_at_[hidden])
Date: 2008-07-15 12:28:33
On Jul 15, 2008, at 11:30 AM, Johan Torp wrote:
> Howard Hinnant wrote:
>> However I did want to clarify one bit: sizeof(unique_ptr<int>) ==
>> sizeof(auto_ptr<int>) ==
>> sizeof(scoped_ptr<int>) == sizeof(int*). And unique_ptr<int> doesn't
>> allocate any extra memory on the heap (unlike shared_ptr, and just
>> like scoped_ptr and auto_ptr).
>> The deleter of unique_ptr is a "static deleter". This is in contrast
>> to shared_ptr's "dynamic deleter". The static deleter requires no
>> overhead unless it contains state (the default_delete is stateless).
> I didn't know this. I suppose this means that:
> 1. All code which potentially destroys a unique_ptr<T> needs to know
> complete declaration of T
> 2. unique_ptr<T> can't be assigned to a unique_ptr<void>
Correct on both counts. Also see LWG issue 762 which is in Ready
which clarifies exactly which members of unique_ptr need T to be
complete and which don't. (anything that delete's the pointer needs
the complete type)
> Statement 1 can substantially increase compilation dependencies if
> who passes around unique_pointers need to know the complete types even
> though they never use them. This type of code is extremely common. For
> instance, to expose dependencies you often have one instance of some
> and pass it all over the application instead of having a singleton.
> pointers, references and shared_ptr need not know the full type of the
> pointer value being passed which removes alot of include statement and
> thereby compilation dependencies. Can this problem be lessened by
> around r-value references?
I don't believe r-value references would help here. Note though that
as long as Base::~Base() is virtual, you can safely pass around a
unique_ptr<Base>(new Derived). Also one can wrap a unique_ptr up in a
pimpl pattern. As long as the pimpl's destructor is outlined, you can
hide the T pretty well.
Essentially the same rules which apply to auto_ptr apply to unique_ptr
(in this department) with one big exception: If you accidently
destruct an incomplete type, unique_ptr will complain at compile
time. auto_ptr just silently accepts this error. The design
philosophy of unique_ptr is: as much functionality as you can get
with no overhead. Let clients who need more functionality obtain it
by building their tools on top of unique_ptr (pay as you go) via
either wrappers or custom deleters.
> Statement 2 can be useful when you only care about objects'
> lifetimes and
> never expect to use it again. I find this pattern very useful,
> for storing a bunch of boost::signal::scoped_connection and other RAII
> objects which you just want to tie to some particular objects
> Anyhow, I suppose unique_ptr<T> can be moved to shared_ptr<void>
> which has
> dynamic destruction (and can be type erased). Am I right?
Yes, unique_ptr<T> can be moved to shared_ptr<void>. That may or may
not be overkill for a given situation. I tend to prefer the zero-
overhead unique_ptr for RAII applications as often as I can get away
with it. Having the customizable deleter makes unique_ptr a lot more
flexible in the RAII department (compared to scoped/auto_ptr), but not
as flexible as shared_ptr.
The use of function-pointer-deleters allows for a nifty "poor-man's
dynamic deleter". A hybrid if you will between the typical unique_ptr
and shared_ptr use cases and overhead:
unique_ptr<int, void(*)(void*)> p1(std::malloc(sizeof(int)), std::free);
unique_ptr<FILE, int(*)(FILE*)> p2(std::fopen("test.dat", "w"),
The function pointer may (for example) point into another dynamic
library. One might build a system whereby you "standardize" on
unique_ptr<void, void(*)(void*)> but construct the unique_ptr with
different function pointers (chosen at run time).
Of course since function pointers aren't stateless, you've swelled the
sizeof unique_ptr to two words instead of one. But there's still no
auxiliary heap access like you would get with shared_ptr.
Here's a little of the original motivation:
The subsection in there titled "Reference Deleter" has a nice RAII
demo where the type of the deleter is not known until instantiation
time and may be stateful (such as counting how many deallocations it
Boost list run by bdawes at acm.org, gregod at cs.rpi.edu, cpdaniel at pacbell.net, john at johnmaddock.co.uk