Для вашего первого блока кода:
std::unique_ptr<Foo>&& self
является ссылкой и присваивает ей аргумент std::move(foo_p)
, где foo_p
является именованным std::unique_ptr<Foo>
, будет связывать только ссылку self
с foo_p
, означающее, что self
будет ссылаться на foo_p
в области вызова.
Он не создает никаких новых std::unique_ptr<Foo>
, в которые может быть передана собственность на управляемый объект Foo
.Построение или назначение перемещения не происходит, и объект Foo
по-прежнему уничтожается с уничтожением foo_p
в вызывающей области.
Поэтому нет риска неопределенного поведения в самом вызове этой функции, хотя вы могли быиспользуйте ссылку self
таким образом, что это может вызвать неопределенное поведение в теле.
Возможно, вы намеревались иметь self
в качестве std::unique_ptr<Foo>
вместо std::unique_ptr<Foo>&&
.В этом случае self
будет не ссылкой, а реальным объектом, для которого владение управляемым Foo
будет передано через конструкцию перемещения при вызове с std::move(p_foo)
и которое будет уничтожено после вызова функции в foo_p->method(std::move(foo_p))
вместе с управляемым Foo
.
Является ли этот альтернативный вариант потенциально неопределенным поведением, зависит от используемой стандартной версии C ++.
До C ++ 17 компилятору было разрешено выбиратьоценить аргументы вызова (и связанную с ним конструкцию перемещения параметра) перед оценкой foo_p->method
.Это будет означать, что foo_p
мог уже сдвинуться с момента оценки foo_p->method
, вызывая неопределенное поведение.Это можно исправить аналогично тому, как вы предлагаете это сделать.
Начиная с C ++ 17 гарантируется, что выражение postfix (здесь foo_p->method
) будет вычислено до того, как любой из аргументов вызова будет ипоэтому сам вызов не будет проблемой.(Тем не менее, тело может вызвать другие проблемы.)
Подробно для последнего случая:
foo_p->method
интерпретируется как (foo_p->operator->())->method
, потому что std::unique_ptr
предлагает это operator->()
.(foo_p->operator->())
разрешает указатель на объект Foo
, управляемый std::unique_ptr
.Последний ->method
разрешает функцию-член method
этого объекта.В C ++ 17 эта оценка происходит перед любой оценкой аргументов в method
и, следовательно, является действительной, потому что еще не произошло перехода от foo_p
.
Тогда порядок оценки аргументов является заданнымнеопределенные.Так что, вероятно, A) unique_ptr foo_p
может быть перемещен из до this
, поскольку аргумент будет инициализирован.И B) it будет перемещаться из ко времени выполнения method
и использовать инициализированные this
.
Но A) не является проблемой, так как § 8.2.2: 4, как и ожидалось:
Если функция является нестатической функцией-членом, параметр this функции должен бытьинициализируется указателем на объект вызова
(И мы знаем, что этот объект был разрешен до любого аргумента.)
И B) не имеет значения как: ( другой вопрос )
спецификация C ++ 11 гарантирует, что передача права собственности на объект от одного unique_ptr к другому unique_ptrне меняет местоположение самого объекта
Для вашего второго блока:
self(std::move(self))
создает лямбда-захват типа std::unique_ptr<Session>
(не ссылка) инициализировансо ссылкой self
, которая ссылается на session_ptr
в лямбде в accept
.Через перемещение-конструкцию право собственности на объект Session
передается от session_ptr
члену лямбды.
Затем лямбда передается в async_read_some
, что будет (поскольку лямбда не передается как неconst lvalue reference) переместить лямбду во внутреннее хранилище, чтобы позже ее можно было вызывать асинхронно.После этого владения объект Session
также переходит к внутренним элементам boost :: asio.
async_read_some
возвращает немедленно, и поэтому все локальные переменные start
и лямбда в accept
уничтожаются.Однако владение Session
уже было передано, и поэтому здесь нет неопределенного поведения из-за проблем с временем жизни.
Асинхронно будет вызываться лямбда-копия, которая может снова вызвать start
, в этом случае владениеSession
будет передан другому члену лямбды, а лямбда с владельцем Session
снова будет перемещена во внутреннее хранилище boost :: asio.После асинхронного вызова лямбды он будет уничтожен boost :: asio.Однако в этот момент, опять же, право собственности уже передано.
Объект Session
окончательно уничтожается, когда происходит сбой if(!errorCode)
и лямбда с владельцем std::unique_ptr<Session>
уничтожается при помощи boost :: asio после егоcall.
Поэтому я не вижу проблем с этим подходом в отношении неопределенного поведения, связанного с временем жизни Session
.Если вы используете C ++ 17, тогда было бы неплохо удалить &&
в параметре std::unique_ptr<Session>&& self
.