Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

About safe operations involving unique pointers

Consider the following code:

#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);
}

By doing foo = std::move(*foo.next);, foo.next.next is moved to foo.next.
If foo.next is invalidated as a first step, the object to which it points could be deleted immediately. This would lead to the deletion of foo.next.next, that is the object that I'm trying to move to foo.next.
I'm pretty sure I'm missing something in my reasoning, but I can't figure out what's wrong.
Is it a safe operation? Where does the standard reassure me about that?

like image 472
skypjack Avatar asked Feb 09 '17 09:02

skypjack


People also ask

What is the use of unique pointer?

Use unique_ptr when you want to have single ownership(Exclusive) of the resource. Only one unique_ptr can point to one resource. Since there can be one unique_ptr for single resource its not possible to copy one unique_ptr to another. A shared_ptr is a container for raw pointers.

How do you dereference a unique pointer?

The unique_ptr shall not be empty (i.e., its stored pointer shall not be a null pointer) in order to be dereferenciable. This can easily be checked by casting the unique_ptr object to bool (see unique_ptr::operator bool). It is equivalent to: *get().

What is smart pointer when should we use it asked me to implement unique_ptr of my own?

std::unique_ptr s are useful when you want to tie the lifetime of the object to a particular block of code, or if you embedded it as member data inside another object, the lifetime of that other object. The object exists until the containing block of code is exited, or until the containing object is itself destroyed.

Where can you use smart pointers?

In modern C++ programming, the Standard Library includes smart pointers, which are used to help ensure that programs are free of memory and resource leaks and are exception-safe.


1 Answers

I think it's all perfectly safe. When you call the f() function on foo, the move assignment operator of class Foo will invoke std::unique_ptr<Foo>::operator=(std::unique_ptr<Foo>&&). Now, the C++14 standard, §20.8.1.2.3, comma 2, says:

Effects: Transfers ownership from u to *this as if by calling reset(u.release()) followed by get_deleter() = std::forward<D>(u.get_deleter()).

At §20.8.1.2.5, comma 4, we find the behavior of reset():

Effects: assigns p to the stored pointer, and then if the old value of the stored pointer, old_p, was not equal to nullptr, calls get_deleter()(old_p). [ Note: The order of these operations is significant because the call to get_deleter() may destroy *this. —end note ]

So, we can argue that the stored pointer will be replaced and then the old stored pointer will be deleted, in this order. Thus, everything is fine and well defined.

Furthermore, when you will enter into the reset() function, the *foo.next object will already have been release()d, so the pointed object wouldn't be destroyed with it.

like image 169
Paolo M Avatar answered Sep 30 '22 21:09

Paolo M