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?
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
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).
The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.