Move unique_ptr: reset the source vs. destroy the old object
Asked Answered
H

1

8

My question is about the example in https://en.cppreference.com/w/cpp/memory/unique_ptr

struct List
{
    struct Node
    {
        int data;
        std::unique_ptr<Node> next;
    };
 
    std::unique_ptr<Node> head;
 
    ~List()
    {
        // destroy list nodes sequentially in a loop, the default destructor
        // would have invoked its `next`'s destructor recursively, which would
        // cause stack overflow for sufficiently large lists.
        while (head)
            head = std::move(head->next);
    }
 
    ...
};

When head = std::move(head->next), there are three things happen:

  1. reset head->next to nullptr;
  2. set head to the new value;
  3. destroy the Node object that head used to point to;

The above code works means 3 is guaranteed to happen after 1, otherwise the chaining still exists.

I would like to know whether there is a written-down rule to guarantee such order, or it is only a hidden implementation details and the example just works.

Hedrick answered 8/4, 2023 at 9:45 Comment(0)
L
7

When head = std::move(head->next), there are three things happen:

  1. reset head->next to nullptr;
  2. set head to the new value;
  3. destroy the Node object that head used to point to;

Assignment operator in this case should work as if head.reset(head->next.release()) function is called, as per [unique.ptr.single.asgn]/3:

constexpr unique_ptr& operator=(unique_ptr&& u) noexcept;

Effects: Calls reset(u.release()) followed by get_deleter() = std​::​forward<D>(u.get_deleter()).

In such an expression head->next.release() has to be evaluated first anyway. reset performs its work in the following order:

  1. Assigns result of head->next.release() to the stored pointer
  2. Deletes the value the pointer stored previously

This order of operations is as well part of the standard, [unique.ptr.single.modifiers]/3:

constexpr void reset(pointer p = pointer()) noexcept;

Effects: Assigns p to the stored pointer, and then, with the old value of the stored pointer, old_p, evaluates if (old_p) get_deleter()(old_p);


Long story short - your understanding is correct and guaranteed by the standard

Leveloff answered 8/4, 2023 at 10:39 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.