Вопрос действительно умещается в заголовке: мне любопытно узнать, в чем техническая причина этой разницы, а также ее обоснование?
std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;
Вопрос действительно умещается в заголовке: мне любопытно узнать, в чем техническая причина этой разницы, а также ее обоснование?
std::shared_ptr<void> sharedToVoid; // legal;
std::unique_ptr<void> uniqueToVoid; // ill-formed;
Это потому, что 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
В комментарии вы задали очень интересный вопрос:
В моем случае мне понадобится средство удаления стирания типа, но это тоже кажется возможным (за счет выделения некоторой кучи). По сути, означает ли это, что на самом деле существует ниша для третьего типа интеллектуального указателя: интеллектуального указателя исключительного владения с стиранием типа.
на который @Steve Jessop предложил следующее решение,
Я никогда не пробовал этого, но, может быть, вы могли бы добиться этого, используя подходящий
std::function
тип удаления сunique_ptr
? Предположим, что это действительно работает, тогда у вас все готово, эксклюзивное право собственности и удаление типа со стиранием.
Следуя этому предложению, я реализовал это (хотя оно не используется, std::function
поскольку не кажется необходимым):
using unique_void_ptr = std::unique_ptr<void, void(*)(void const*)>;
template<typename T>
auto unique_void(T * ptr) -> unique_void_ptr
{
return unique_void_ptr(ptr, [](void const * data) {
T const * p = static_cast<T const*>(data);
std::cout << "{" << *p << "} located at [" << p << "] is being deleted.\n";
delete p;
});
}
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.
Надеюсь, это поможет.
std::unique_ptr<void, D>
прежнему возможно, предоставив подходящийD
.std::function
тип удаления сunique_ptr
? Предположим, что это действительно работает, тогда у вас все готово, эксклюзивное право собственности и удаление типа со стиранием.Одно из объяснений заключается в одном из многих случаев использования a,
shared_ptr
а именно в качестве индикатора срока службы или дозорного.Об этом упоминалось в исходной документации по ускорению:
Где
closure_target
что-то вроде этого:Вызывающий абонент регистрирует обратный вызов примерно так:
поскольку
shared_ptr<X>
он всегда конвертируетсяshared_ptr<void>
, event_emitter теперь может быть в блаженном неведении о типе объекта, в который он вызывает.Эта договоренность освобождает подписчиков эмиттера событий от обязанности обрабатывать случаи пересечения (что, если обратный вызов находится в очереди, ожидая выполнения действия, пока active_object уйдет?), А также означает, что нет необходимости синхронизировать отписку.
weak_ptr<void>::lock
это синхронизированная операция.источник