О безопасных операциях с уникальными указателями

Рассмотрим следующий код:

#include <memory>

struct Foo { std::unique_ptr<Foo> next; };
void f(Foo &foo) { foo = std::move(*foo.next); }

int main() {
    Foo foo{};
    foo.next = std::make_unique<Foo>();
    foo.next->next = std::make_unique<Foo>();
    f(foo);
}

Выполняя foo = std::move(*foo.next);, foo.next.next перемещается в foo.next.
Если foo.next недействителен как первый шаг, объект, на который он указывает, может быть удален немедленно. Это приведет к удалению foo.next.next, то есть объекта, который я пытаюсь переместить в foo.next.
Я почти уверен, что мне что-то не хватает в моих рассуждениях, но я не могу понять, что не так. Это безопасная операция? Где стандарт успокаивает меня об этом?

Ответ 1

Я думаю, все это совершенно безопасно. Когда вы вызываете функцию f() на foo, оператор присваивания перемещения class Foo будет вызывать std::unique_ptr<Foo>::operator=(std::unique_ptr<Foo>&&). Теперь, стандарт С++ 14, §20.8.1.2.3, запятая 2, говорит:

Эффекты: переносит право собственности с u на *this, как если бы он вызывал reset(u.release()), а затем get_deleter() = std::forward<D>(u.get_deleter()).

В § 20.8.1.2.5, запятая 4, находим поведение reset():

Эффекты: назначает p сохраненному указателю, а затем, если старое значение сохраненного указателя old_p не равно nullptr, вызывает get_deleter()(old_p). [Примечание: порядок этих операций значителен, потому что вызов get_deleter() может уничтожить *this. -end note]

Итак, мы можем утверждать, что сохраненный указатель будет заменен, а , а затем старый сохраненный указатель будет удален в этом порядке. Таким образом, все хорошо и четко определено.

Кроме того, когда вы войдете в функцию reset(), объект *foo.next уже будет release() d, поэтому указанный объект не будет уничтожен вместе с ним.