Ответ 1
Это потому, что std::shared_ptr
реализует стирание типа, а std::unique_ptr
- нет.
Поскольку std::shared_ptr
реализует стирание типов, он также поддерживает еще одно интересное свойство, а именно. ему не нужен тип удалителя в качестве аргумента типа шаблона для шаблона класса. Посмотрите на их заявления:
template<class T,class Deleter = std::default_delete<T> >
class unique_ptr;
который имеет Deleter
качестве параметра типа, в то время как
template<class T>
class shared_ptr;
не имеет его
Теперь возникает вопрос: почему shared_ptr
реализует стирание типов? Что ж, это так, потому что он должен поддерживать подсчет ссылок, и для поддержки этого он должен выделять память из кучи, и поскольку он должен выделять память в любом случае, он делает еще один шаг вперед и реализует стирание типов - что требует кучи распределение тоже. Так что в основном это просто оппортунист!
Из-за стирания типа std::shared_ptr
может поддерживать две вещи:
- Он может хранить объекты любого типа как
void*
, но при этом он может правильно удалять объекты при уничтожении , правильно вызывая их деструктор. - Тип средства удаления не передается в качестве аргумента типа в шаблон класса, что означает небольшую свободу без ущерба для безопасности типов.
Хорошо. Это все о том, как работает std::shared_ptr
.
Теперь вопрос в том, может ли std::unique_ptr
хранить объекты как void*
? Ну, ответ - да - при условии, что вы передадите подходящий аргумент в качестве аргумента. Вот одна из таких демонстраций:
int main()
{
auto deleter = [](void const * data ) {
int const * p = static_cast<int const*>(data);
std::cout << *p << " located at " << p << " is being deleted";
delete p;
};
std::unique_ptr<void, decltype(deleter)> p(new int(959), deleter);
} //p will be deleted here, both p ;-)
Выход (онлайн демо):
959 located at 0x18aec20 is being deleted
Вы задали очень интересный вопрос в комментарии:
В моем случае мне понадобится стирающее стирание типа, но это также кажется возможным (за счет некоторого выделения кучи). По сути, означает ли это, что на самом деле есть ниша для интеллектуального указателя 3-го типа: интеллектуального указателя с исключительным владением и стирания типа.
на который @Steve Jessop предложил следующее решение,
На самом деле я никогда не пробовал этого, но, возможно, вы могли бы добиться этого, используя соответствующую
std::function
в качестве типа удалителя сunique_ptr
? Предположим, что это действительно работает, то все готово, исключительное право собственности и удаленный тип.
Следуя этому предложению, я реализовал это,
using unique_void_ptr = std::unique_ptr<void, void(*)(void const*)>;
template<typename T>
auto deleter(void const * data) -> void
{
T const * p = static_cast<T const*>(data);
std::cout << "{" << *p << "} located at [" << p << "] is being deleted.\n";
delete p;
}
template<typename T>
auto unique_void(T * ptr) -> unique_void_ptr
{
return unique_void_ptr(ptr, &deleter<T>);
}
int main()
{
auto p1 = unique_void(new int(959));
auto p2 = unique_void(new double(595.5));
auto p3 = unique_void(new std::string("Hello World"));
}
Выход (онлайн демо):
{Hello World} located at [0x2364c60] is being deleted.
{595.5} located at [0x2364c40] is being deleted.
{959} located at [0x2364c20] is being deleted.
Надеюсь, это поможет.