Нужно переместить владение из одного непустого std::unique_ptr в другой уже владеющий ресурсом. Как изменится судьба обоих ресурсов?
При move-присваивании старый ресурс объекта-получателя сначала освобождается его deleter-ом, после чего получатель принимает ресурс источника. Исходный std::unique_ptr теряет владение и становится пустым; сам ресурс при этом не уничтожается из-за перемещения.
Владение динамическими ресурсами долгое время передавалось через сырые указатели, что не показывало намерение владельца и легко приводило к двойному освобождению или утечкам. std::unique_ptr, появившийся в C++11, сделал единоличное владение частью типа и использовал перемещение для явной передачи ответственности.
Такой подход заменяет неявный договор между функциями проверяемой операцией: копирование запрещено, а перемещение означает передачу владения.
Если целевой std::unique_ptr уже владеет ресурсом, простая замена внутреннего указателя без предварительного освобождения привела бы к утечке старого ресурса. Если же после перемещения исходный указатель продолжал бы считаться владельцем, один ресурс оказался бы у двух владельцев и мог быть освобождён дважды.
Корректный результат должен сохранить единственного владельца: старый ресурс получателя уничтожается, ресурс источника переходит получателю, а источник больше не должен использоваться для доступа к этому ресурсу.
Операция move-присваивания логически выполняет следующие действия:
Перемещается не сам объект ресурса, а дескриптор владения: обычно указатель и, возможно, состояние пользовательского deleter-а. Поэтому адрес объекта, которым владел источник, не меняется.
После присваивания значение 20 уничтожено, target владеет объектом со значением 10, а source пуст. Обращаться к объекту через source нельзя, но сам перемещённый из него std::unique_ptr можно уничтожить, сбросить или заново присвоить ему другой ресурс.
Операция std::move сама по себе ничего не перемещает: она только позволяет выбрать перегрузку, принимающую rvalue-ссылку. Реальное изменение владения выполняет оператор перемещения std::unique_ptr.
Важна совместимость deleter-ов. Move-присваивание между std::unique_ptr обычно требует, чтобы deleter целевого типа поддерживал необходимое присваивание; пользовательский deleter может влиять также на noexcept и размер std::unique_ptr. Если требуется просто обменять два владения без уничтожения ресурса, используют swap, а не последовательное ручное освобождение.
Компонент заменяет открытый файл новым. У него уже есть std::unique_ptr с пользовательским deleter-ом для старого файлового дескриптора, а функция открытия возвращает новый std::unique_ptr.
Ручной вызов reset с последующим извлечением сырого указателя возможен, но увеличивает промежуток, в котором легко потерять владение или допустить исключение. Использование std::shared_ptr неоправданно: совместное владение не требуется и добавляет контрольный блок со счётчиком ссылок.
Выбранное решение — move-присваивание нового std::unique_ptr существующему. Старый дескриптор закрывается автоматически, новый становится единственным владельцем, а локальный источник безопасно уничтожается в пустом состоянии. Это уменьшает количество ручных операций и сохраняет гарантию освобождения при выходе из области видимости.
Уничтожается ли ресурс источника непосредственно из-за move-присваивания?
Нет. Move-присваивание передаёт владение, поэтому ресурс, которым владел источник, продолжает существовать и затем уничтожается при уничтожении нового владельца. Непосредственно во время присваивания обычно уничтожается только прежний ресурс получателя.
Что произойдёт, если целевой std::unique_ptr был пустым?
Освобождать нечего. Он просто примет ресурс источника, а источник станет пустым. Таким образом, отличие от присваивания в непустой объект состоит только в отсутствии уничтожения прежнего ресурса получателя.
Можно ли после перемещения разыменовать исходный std::unique_ptr?
Нет. После корректного перемещения исходный std::unique_ptr не владеет переданным ресурсом и обычно содержит nullptr; разыменование было бы ошибкой. Его разрешено проверять на пустоту, уничтожать, вызывать у него reset или присваивать ему новое владение.