Что означает передача std::unique_ptr в функцию по значению с точки зрения владения ресурсом?
Передача std::unique_ptr по значению означает, что функция получает отдельный объект-владелец и становится владельцем ресурса. Для вызова обычно требуется явно переместить указатель через std::move; после этого исходный std::unique_ptr сохраняется, но больше не владеет ресурсом.
Если функция завершится, её локальный std::unique_ptr уничтожится и освободит ресурс, если владение не было передано дальше. Копирование невозможно, поэтому случайная передача владения без явного намерения не происходит.
До появления стандартных средств управления ресурсами владение часто передавали через сырые указатели и неформальные договорённости. Компилятор не мог отличить указатель-владелец от указателя, который лишь временно обращается к объекту, поэтому ошибки освобождения и утечек обнаруживались поздно.
В C++11 появился std::unique_ptr — перемещаемый, но некопируемый RAII-владелец. Его интерфейс делает передачу владения частью типа и позволяет связать освобождение ресурса с временем жизни объекта-владельца.
Функция может либо только использовать ресурс, либо принять ответственность за его дальнейшее освобождение. Если эти намерения не отражены в интерфейсе, вызывающий код не понимает, останется ли он владельцем после вызова.
Передача std::unique_ptr по значению явно моделирует смену владельца. Нельзя передать его обычным копированием: две независимые копии не должны одновременно пытаться освободить один ресурс.
При вызове по значению параметр инициализируется перемещением из аргумента. Перемещение передаёт хранимый адрес и связанные с ним сведения об удалении новому std::unique_ptr, а исходный объект переводит в пустое состояние. У исходного объекта остаётся корректное состояние, но владения ресурсом уже нет.
Минимальный пример:
Если consume не передаст ресурс дальше, уничтожение value вызовет удаление объекта. Если функция должна вернуть владение, она может вернуть std::unique_ptr по значению: возврат также использует перемещение или гарантированное устранение лишнего объекта.
Для функции, которая только наблюдает объект и не должна менять владельца, обычно применяют ссылку или ссылку на const к самому ресурсу, а не передачу std::unique_ptr по значению. Ссылка на std::unique_ptr нужна, когда функция должна работать именно с владельцем: например, заменить его или проверить его состояние.
Передача по значению имеет компромисс: интерфейс хорошо выражает принятие владения, но вызывающий код должен явно написать std::move. После перемещения нельзя разыменовывать исходный указатель без предварительной проверки; его можно уничтожить, присвоить ему новый ресурс или проверить на пустоту.
Сервис создаёт временный объект и передаёт его компоненту, которая должна сохранить объект в своём состоянии. Рассматривались три варианта: передача сырого указателя не выражает владение и допускает утечку; std::shared_ptr позволяет совместное владение, но добавляет счётчик ссылок и не нужен при единственном владельце; передача std::unique_ptr по значению требует явного перемещения и точно фиксирует смену ответственности.
Выбран третий вариант: фабрика возвращает std::unique_ptr, а компонент принимает его по значению и сохраняет перемещением в поле. В результате владелец всегда один, освобождение выполняется автоматически при уничтожении компонента, а передача владения видна непосредственно в месте вызова.
Можно ли передать std::unique_ptr по значению без std::move?
Для именованного объекта — нет, потому что именованное выражение является lvalue, а копирующий конструктор std::unique_ptr удалён. Вызов с таким аргументом не скомпилируется. std::move не перемещает память сам по себе: он лишь позволяет выбрать перемещающий конструктор, который передаёт владение.
Что произойдёт, если функция примет владение, но завершится исключением?
Если параметр уже успешно создан, он уничтожится во время раскрутки стека. Его RAII-деструктор освободит ресурс, поэтому обычное исключение не приводит к утечке. Однако если функция должна сохранить ресурс после ошибки, она обязана передать владение другому объекту до возникновения исключения или вернуть его через подходящий интерфейс.
Чем передача по значению отличается от параметра std::unique_ptr&&?
std::unique_ptr&& — это ссылка на существующий объект-владелец; сама по себе она владение не принимает. Функция может переместить ресурс из такого параметра, но может и не сделать этого, оставив владельца у вызывающего кода. Передача по значению сразу создаёт локального владельца, поэтому семантика принятия владения проще для чтения и безопаснее при дальнейшей передаче или сохранении.