Why does unique_ptr have the deleter as a type parameter while shared_ptr doesn't?

The std::unique_ptr template has two parameters: the type of the pointee, and the type of the deleter. This second parameter has a default value, so you usually just write something like std::unique_ptr<int>.

The std::shared_ptr template has only one parameter though: the type of the pointee. But you can use a custom deleter with this one too, even though the deleter type is not in the class template. The usual implementation uses type erasure techniques to do this.

Is there a reason the same idea was not used for std::unique_ptr?


Part of the reason is that shared_ptr needs an explicit control block anyway for the ref count and sticking a deleter in isn't that big a deal on top. unique_ptr however doesn't require any additional overhead, and adding it would be unpopular- it's supposed to be a zero-overhead class. unique_ptr is supposed to be static.

You can always add your own type erasure on top if you want that behaviour- for example, you can have unique_ptr<T, std::function<void(T*)>>, something that I have done in the past.


Another reason, in addition to the one pointed out by DeadMG, would be that it's possible to write

std::unique_ptr<int[]> a(new int[100]);

and ~unique_ptr will call the correct version of delete (via default_delete<_Tp[]>) thanks to specializing for both T and T[].


From C++ Primer (5th Edition), Chapter 16.1.6 - Efficiency and Flexibility

The obvious difference between shared_ptr and unique_ptr is the strategy they use in managing the pointer they hold—one class gives us shared ownership; the other owns the pointer that it holds. This difference is essential to what these classes do.

These classes also differ in how they let users override their default deleter. We can easily override the deleter of a shared_ptr by passing a callable object when we create or reset the pointer. In contrast, the type of the deleter is part of the type of a unique_ptr object. Users must supply that type as an explicit template argument when they define a unique_ptr. As a result, it is more complicated for users of unique_ptr to provide their own deleter.

By binding the deleter at compile time, unique_ptr avoids the run-time cost of an indirect call to its deleter. By binding the deleter at run time, shared_ptr makes it easier for users to override the deleter.