Почему unique_ptr не может определить тип делетера?

Скажем, я хочу использовать пользовательский делетер с уникальным_ptr:

void custom_deleter(int* obj)
{
    delete obj; 
}

Зачем мне это писать:

std::unique_ptr<int, void(*)(int*)> x(new int, custom_deleter);

вместо этого:

std::unique_ptr<int> x(new int, custom_deleter); //does not compile

?

Нельзя ли вывести тип делетера?

Ответ 1

Для unique_ptr делетер является частью типа:

template <
    class T,
    class Deleter = std::default_delete<T>
> class unique_ptr;

Таким образом, когда вы строите объект, вам нужно указать его тип. Строка, которую вы пишете:

std::unique_ptr<int> x(new int, custom_deleter);

эквивалентно:

std::unique_ptr<int, std::default_delete<int> > x(new int, custom_deleter);

И вы не можете построить std::default_delete<int> из custom_deleter.

Единственный способ сделать вывод типа делетира - использовать вычитание шаблона на этой части:

template <typename T, typename Deleter>
std::unique_ptr<T, Deleter> make_unique_ptr(T* ptr, Deleter deleter) {
    return std::unique_ptr<T, Deleter>(ptr, deleter);
}

Ответ 2

Он не может вывести тип делетера, потому что unique_ptr по умолчанию не имеет состояния, посвященного делетеру: дефолт по умолчанию не имеет состояния.

В вашем случае для делетера требуется значение состояния указателя, поэтому оно не может "соответствовать" в состоянии std::unique_ptr (которое является только указателем на T).

Это делает unique_ptr легкую, почти бесплатную замену для владеющего указателя.

Дедуктин может быть выполнен, но он должен будет изменить тип результирующего unique_ptr.

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