За std::unique_ptr
s p1
а также p2
Каковы различия между std::move()
а также std::unique_ptr::reset()
?
p1 = std::move(p2);
p1.reset(p2.release());
Ответ должен быть очевиден из спецификации стандарта на перемещение в [unique.ptr.single.assign] / 2:
Последствия: Передача права собственности от
u
в*this
как будто позвонивreset(u.release())
с последующим назначением изstd::forward<D>(u.get_deleter())
,
Ясно, что задание на перемещение не совпадает с reset(u.release())
потому что это делает что-то дополнительное.
Дополнительный эффект важен, без него вы можете получить неопределенное поведение с пользовательскими удалителями:
#include <cstdlib>
#include <memory>
struct deleter
{
bool use_free;
template<typename T>
void operator()(T* p) const
{
if (use_free)
{
p->~T();
std::free(p);
}
else
delete p;
}
};
int main()
{
std::unique_ptr<int, deleter> p1((int*)std::malloc(sizeof(int)), deleter{true});
std::unique_ptr<int, deleter> p2;
std::unique_ptr<int, deleter> p3;
p2 = std::move(p1); // OK
p3.reset(p2.release()); // UNDEFINED BEHAVIOUR!
}
Первый способ предупредить вас, например, о несоответствии деструкторов. К тому же, release()
это очень опасная функция, и ваш тривиальный пример верен, но многие другие применения — нет. Лучше просто никогда, никогда не использовать эту функцию.
Думаю, вторая версия может быть небезопасной. Это эквивалентно:
auto __tmp = p2.release();
p1.reset(__tmp);
Таким образом, если вызов std::unique_ptr::reset
throws (что может иметь место в случае удаления бросков управляемого объекта), тогда у вас есть объект без ссылки, который никогда не будет уничтожен. В случае назначения перемещения, std::unique_ptr
может (и должен) ждать фактического движения до p1
Оригинальный объект был уничтожен должным образом.
Но обратите внимание, что это проблема только в том случае, если деструктор управляемого объекта может сгенерировать, что почти во всех случаях само по себе неверно, или если вы используете специальный удалитель, который может сгенерировать. Таким образом, на практике обычно нет никаких поведенческих различий между двумя фрагментами кода.
РЕДАКТИРОВАТЬ: В конце Джонатан в своем комментарии указывает на то, что стандартное средство удаления требует от пользователя не выбрасывать, что действительно делает std::unique_ptr::reset
довольно маловероятно / не соответствует. Но он также указывает на то, что есть еще одно отличие в том, что только назначение перемещения также перемещает любые пользовательские средства удаления, для которых он также написал ответ.
Но если не учитывать фактическое поведение, между ними существует огромная концептуальная разница. Если назначение перемещения подходит, затем сделайте задание на перемещение и постарайтесь не эмулировать его другим кодом. На самом деле я не могу представить себе причину заменить первый фрагмент кода один на один вторым. DeadMG прав в этом std::unique_ptr::release
следует использовать, только если вы действительно знаете, что делаете и в каком контексте вы связываетесь с неуправляемыми динамическими объектами.