Why does unique_ptr have the deleter as a type parameter while shared_ptr doesn't?
C++Smart PointersType ErasureC++ Problem Overview
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
?
C++ Solutions
Solution 1 - C++
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.
Solution 2 - C++
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[]
.
Solution 3 - C++
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.