
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 the complete declaration of T 2. unique_ptr<T> can't be assigned to a unique_ptr<void>
Correct?
Correct on both counts. Also see LWG issue 762 which is in Ready status: http://www.open-std.org/jtc1/sc22/wg21/docs/lwg-active.html#762 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 everybody 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 class and pass it all over the application instead of having a singleton. Raw 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 passing 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, especially for storing a bunch of boost::signal::scoped_connection and other RAII objects which you just want to tie to some particular objects lifetime. 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"), std::fclose); etc. 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: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1856.html#Addition%... 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 has made). -Howard