Search code examples
c++11sizeshared-ptrsmart-pointersunique-ptr

Is a unique_ptr equipped with a function pointer as custom deleter the same size as a shared_ptr?


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?


Solution

  • 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.