using Ptr = std::unique_ptr<int>;
Ptr f(bool arg) {
std::list<Ptr> list;
Ptr ptr(new int(1));
list.push_back(std::move(ptr));
if (arg) {
Ptr&& obj1 = std::move(list.front());
// Here |obj1| and |list.front()| still point to the same location!
list.pop_front();
return std::move(obj1);
}
else {
Ptr obj2 = std::move(list.front());
list.pop_front();
return obj2;
}
};
Ptr&& ptr1 = f(true); // |ptr1| is empty.
Ptr&& ptr2 = f(false); // |ptr2| is fine.
The full source is here.
I don't understand — why do obj1
and list.front()
still point to the same location after std::move()
is called?
A unique_ptr can only be moved. This means that the ownership of the memory resource is transferred to another unique_ptr and the original unique_ptr no longer owns it. We recommend that you restrict an object to one owner, because multiple ownership adds complexity to the program logic.
std::unique_ptr is a smart pointer that owns and manages another object through a pointer and disposes of that object when the unique_ptr goes out of scope. The object is disposed of, using the associated deleter when either of the following happens: the managing unique_ptr object is destroyed.
Yes, you can compare it to nullptr after the move and it is guaranteed to compare equal. This is clearly true after calling release().
unique_ptr is not copyable, it is only moveable. This will directly affect Test, which is, in your second, example also only moveable and not copyable. In fact, it is good that you use unique_ptr which protects you from a big mistake.
You have a reference to the std::unique_ptr
inside list
.
Ptr&& obj1 = std::move(list.front());
// ^^ THIS
And so when you do
list.pop_front();
The unique pointer that's in list
gets destroyed, and you are left with a dangling reference to some object that is already destroyed, which is illegally used by returning it from the function.
UPDATE: std::move
there doesn't actually move the std::unique_ptr
. If we see how std::move
is defined, we see that it only returns an r-value reference of the expression being moved. It becomes interesting when you test for it:
Ptr&& obj1 = std::move(list.front());
assert(obj1.get() == list.front().get());
Your else
clause does seems fine though, and you should use that pattern for the rest of your code.
Ptr obj2 = std::move(list.front());
list.pop_front(); // OK; destroys the moved-from unique_ptr
return obj2; // OK; obj2 points to something valid and is not a dangling reference
@nosid has summed-up what I want this answer to mean.
Without move-semantics std::unique_ptr
is less useful. With move-semantics it allows transfer of ownership to another object.
With list.push_back(std::move(ptr));
you're transferring the ownership of the data to a new element and leaving ptr in a nullptr state (read here).
After that, if arg is true, since list.front()
returns a reference to the first element in the container, std::move
takes an r-value out of it and feeds it to the r-value reference obj1. Notice that you're not transferring ownership to another object since you're only asking for a r-value reference to the data. An r-value reference is, in the end, a reference to a value.
In the specific case above, regarding the cout statement, it is equivalent to getting just a simple reference to the object
Ptr& obj1 = list.front();
Now you have both the element into the list and the obj1 r-value reference "pointing" to the same data and modifying one will cause modifying both
Ptr&& obj1 = std::move(list.front());
std::cout << obj1.get() << std::endl << list.front().get() << std::endl; // same address
obj1.reset(new int(2));
std::cout << obj1.get() << std::endl << list.front().get() << std::endl; // same other address
if you were, as in the false case, to do
Ptr obj1 = std::move(list.front());
then you would have had the ownership (and thus the nullptr-ify of the list object) transfer to obj1.
If you followed the above reasoning, you can also realize that the line
list.pop_front();
destroyes the unique_ptr object and leaves the r-value reference into an undefined state. You shouldn't really be returning it (and using it).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With