Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How can unique_ptr have no overhead if it needs to store the deleter?

First take a look at what C++ Primer said about unique_ptr and shared_ptr:
$16.1.6. Efficiency and Flexibility

We can be certain that shared_ptr does not hold the deleter as a direct member, because the type of the deleter isn’t known until run time.

Because the type of the deleter is part of the type of a unique_ptr, the type of the deleter member is known at compile time. The deleter can be stored directly in each unique_ptr object.

So it seems like that the shared_ptr does not have a direct member of deleter, but unique_ptr does. However, the top-voted answer of another question says:

If you provide the deleter as template argument (as in unique_ptr) it is part of the type and you don't need to store anything additional in the objects of this type. If deleter is passed as constructor's argument (as in shared_ptr) you need to store it in the object. This is the cost of additional flexibility, since you can use different deleters for the objects of the same type.

The two quoted paragraph are totally conflicting, which makes me confused. What's more, many people says unique_ptr is zero overhead because it doesn't need to store the deleter as member. However, as we know, unique_ptr has a constructor of unique_ptr<obj,del> p(new obj,fcn), which means that we can pass a deleter to it, so unique_ptr seems to have stored deleter as a member. What a mess!

like image 423
scottxiao Avatar asked Apr 24 '18 12:04

scottxiao


People also ask

What happens when unique_ptr goes out of scope?

unique_ptr. An​ unique_ptr has exclusive ownership of the object it points to and ​will destroy the object when the pointer goes out of scope.

Do you need to delete a unique_ptr?

An explicit delete for a unique_ptr would be reset() . But do remember that unique_ptr are there so that you don't have to manage directly the memory they hold. That is, you should know that a unique_ptr will safely delete its underlying raw pointer once it goes out of scope.

Why would you choose shared_ptr instead of unique_ptr?

In short: Use unique_ptr when you want a single pointer to an object that will be reclaimed when that single pointer is destroyed. Use shared_ptr when you want multiple pointers to the same resource.

Can you pass unique_ptr by value?

A unique_ptr does not share its pointer. It cannot be copied to another unique_ptr , passed by value to a function, or used in any C++ Standard Library algorithm that requires copies to be made. A unique_ptr can only be moved.


3 Answers

std::unique_ptr<T> is quite likely to be zero-overhead (with any sane standard-library implementation). std::unique_ptr<T, D>, for an arbitrary D, is not in general zero-overhead.

The reason is simple: Empty-Base Optimisation can be used to eliminate storage of the deleter in case it's an empty (and thus stateless) type (such as std::default_delete instantiations).

like image 130
Angew is no longer proud of SO Avatar answered Oct 19 '22 17:10

Angew is no longer proud of SO


The key phrase which seems to confuse you is "The deleter can be stored directly". But there's no point in storing a deleter of type std::default_delete. If you need one, you can just create one as std::default_delete{}.

In general, stateless deleters do not need to be stored, as you can create them on demand.

like image 45
MSalters Avatar answered Oct 19 '22 17:10

MSalters


Angew's answer explained pretty thoroughly what's going on.

For those curious how things could look under the covers

template<typename T, typename D, bool Empty = std::is_empty_v<D>>
class unique_ptr
{
    T* ptr;
    D d;

    // ... 
};

template<typename T, typename D>
class unique_ptr<T, D, true> : D
{
    T* ptr;

    // ...
};

Which specializes for empty deleters and take advantage of empty base optimization.

like image 11
Passer By Avatar answered Oct 19 '22 18:10

Passer By