I know that std::unique_ptr
and std::shared_ptr
are different classes that address different needs, and therefore asking which one is better is mostly an ill-posed question.
However, as regards their content and performance, without considering the different semantics of the two smart pointers, I have some doubt I want clarify.
My understanding is that a std::unique_ptr
contains the raw pointer as its only member variable, and stores the deleter, if any custom one is given, as part of the type; whereas the std::shared_ptr
stores in member variables the raw as well as the pointer to a dynamically allocated block which contains the custom deleter, and strong and weak counters.
Scott Meyers, in Effective Modern C++, stresses a lot on this difference in the size that the two smart pointers require (and on the difference in performance in general), however I'd be tempted to say that as soon as a std::unique_ptr
is provided with a function pointer as custom deleter it becomes as big as std::shared_ptr
, whose size does not increase with the custom deleter.
From there, I would deduce that using a function pointer as a custom deleter for std::unique_ptr
basically annihilates the advantage this smart pointer has on the other one, in terms of size/performance.
Is this the case?
It is true (I doubt that this is required by the standard though) that
static_assert(sizeof(std::unique_ptr<int, void(*)(int*)>) == sizeof(std::shared_ptr<int>));
but I don't agree with the conclusion that storing a function pointer as the custom deleter renders the advantages of std::unique_ptr
over std::shared_ptr
useless. Both types model very different ownership semantics, and choosing one over the other has not so much to do with performance, but rather with how you intend to handle the pointee instances.
Performance-wise, std::unique_ptr
will always be more efficient than std::shared_ptr
. While this is primarily due to thread-safe reference counting of the latter, it is also true for custom deleters:
std::unique_ptr
stores the deleter in-place, i.e., on the stack. Invoking this deleter is likely to be faster than one that lives in a heap-allocated block together with pointee and reference count.std::unique_ptr
also doesn't type-erase the deleter. A lambda that is baked into the type is certainly more efficient than the indirection required to hide the deleter type as in std::shared_ptr
.