28

I thought it is very curious when I discovered that the standard defines std::unique_ptr and std::shared_ptr in two totally different ways regarding a Deleter that the pointer may own. Here is the declaration from cppreference::unique_ptr and cppreference::shared_ptr:

template<
    class T,
    class Deleter = std::default_delete<T>
> class unique_ptr;

template< class T > class shared_ptr;

As you can see the unique_ptr "saves" the type of the the Deleter-object as a template argument. This can also be seen in the way the Deleter is retrieved from the pointer later on:

// unique_ptr has a member function to retrieve the Deleter
template<
    class T,
    class Deleter = std::default_delete<T>
>
Deleter& unique_ptr<T, Deleter>::get_deleter();

// For shared_ptr this is not a member function
template<class Deleter, class T>
Deleter* get_deleter(const std::shared_ptr<T>& p);

Can someone explain the rational behind this difference? I clearly favor the concept for unique_ptr why is this not applied to shared_ptr aswell? Also, why would get_deleter be a non-member function in the latter case?

WorldSEnder
  • 4,875
  • 2
  • 28
  • 64
  • 2
    Someone will have to dig up the original proposal, but my educated guesses: Not having the deleter as the template argument makes `shared_ptr` easier to use, but you need to pay the type erasure costs. Making `get_deleter` a member will make writing generic code taking a `shared_ptr` more tedious - you'd need to write `sp.template get_deleter()` instead of `get_deleter(sp)`. This is why `std::get` is a nonmember. – T.C. Jan 02 '15 at 12:36
  • 3
    Expanding slightly on what @T.C. said, one of the design goals for `unique_ptr` is that it should have (very nearly) zero overhead. Erasing the deleter's type is convenient but introduces run time overhead from the erasure, so it is less appropriate for `unique_ptr` than for `shared_ptr` – wakjah Jan 02 '15 at 12:40
  • You should also note that because of that difference, `shared_ptr p = make_shared()` does the right thing even if `Base` has no virtual destructor. [proof](http://coliru.stacked-crooked.com/a/f3a50f90e00d4e58). – Benoît Jan 06 '15 at 09:58

1 Answers1

27

Here you can find the original proposal for smart pointers: http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2003/n1450.html

It answers your question quite precisely:

Since the deleter is not part of the type, changing the allocation strategy does not break source or binary compatibility, and does not require a client recompilation.

This is also useful because gives the clients of std::shared_ptr some more flexibility, for example shared_ptr instances with different deleters can be stored in the same container.

Also, because the shared_ptr implementations needs a shared memory block anyhow (for storing the reference count) and because there alreay has to be some overhead compared to raw pointers, adding a type-erased deleter is not much of a big deal here.

unique_ptr on the other hand are inteded to have no overhead at all and every instance has to embed its deleter, so making it a part of the type is the natural thing to do.

Curious
  • 20,870
  • 8
  • 61
  • 146
Horstling
  • 2,131
  • 12
  • 14
  • How does the type-erased deleter get called btw? – WorldSEnder Jan 02 '15 at 13:21
  • @WorldSEnder: This is up to the implementation, but the usual way is to encapsulate the concrete deleter into a templated class that implements (inherits) a interface for deleting a T*. So when the reference count reaches zero, the implementation calls the virtual method, this call is dispatched to the embedding class which in turn calls the concrete deleter. It's the same mechanism as for std::function. – Horstling Jan 02 '15 at 13:31
  • 1
    Note that 'raw' lower level implementation of `std::function` and probably `shared_ptr` will be faster in practice than a vtable based one, but few std libraries use them (see 'fastest possible delegates' via google). The vtable implementation is just the easiest to understand, as it looks like normal C++. – Yakk - Adam Nevraumont Jan 02 '15 at 13:45