Почему unique_ptr принимает два параметра шаблона, когда shared_ptr принимает только один?

Оба unique_ptr и shared_ptr принимают пользовательский деструктор для вызова объекта, которому они принадлежат. Но в случае unique_ptr деструктор передается как шаблонный параметр класса, тогда как тип shared_ptr настраиваемого деструктора должен быть указан как шаблонный шаблон конструктора.

template <class T, class D = default_delete<T>> 
class unique_ptr
{
    unique_ptr(T*, D&); //simplified
    ...
};

и

template<class T>
class shared_ptr
{
    template<typename D>
    shared_ptr(T*, D); //simplified
    ...
};

Я не понимаю, почему такая разница. Что требует этого?

Ответ 1

Если вы предоставляете deleter в качестве аргумента шаблона (как в unique_ptr), он является частью типа, и вам не нужно хранить что-либо дополнительно в объектах этого типа. Если deleter передается как аргумент конструктора (как в shared_ptr), вам нужно сохранить его в объекте. Это является дополнительной гибкостью, поскольку вы можете использовать разные удалители для объектов того же типа.

Я предполагаю, что это причина: unique_ptr должен быть очень легким объектом с нулевыми служебными данными. Сохранение дескрипторов с каждым unique_ptr может удвоить их размер. Из-за этого люди вместо этого использовали бы добрые старые исходные указатели, что было бы неправильно.

С другой стороны, shared_ptr не является таким легким, так как ему нужно хранить подсчет ссылок, поэтому хранение пользовательского делетера также выглядит как хороший компромисс.