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.
完整来源为here。
我不明白 - 为什么obj1
和list.front()
在调用std::move()
后仍指向同一位置?
答案 0 :(得分:4)
您对std::unique_ptr
内的list
有参考。
Ptr&& obj1 = std::move(list.front());
// ^^ THIS
所以当你这样做时
list.pop_front();
list
中的唯一指针被破坏,你留下一个已经被破坏的对象的悬空引用,这是从函数返回非法使用的。
更新: std::move
实际上并没有移动std::unique_ptr
。如果我们看到how std::move
is defined,我们会看到它只返回正在移动的表达式的r值引用。当你测试它时会变得很有趣:
Ptr&& obj1 = std::move(list.front());
assert(obj1.get() == list.front().get());
您的else
子句似乎确实很好,您应该将该模式用于其余代码。
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
答案 1 :(得分:1)
没有move-semantics std::unique_ptr
就不那么有用了。使用移动语义,它允许将所有权转移到另一个对象。
使用list.push_back(std::move(ptr));
,您将数据的所有权转移到新元素,并将ptr保留在 nullptr 状态(read here)。
之后,如果arg为true,则list.front()
returns a reference to the first element in the container,std::move
从中获取r值并将其提供给r值引用obj1。请注意,您没有将所有权转移到另一个对象,因为您只要求对数据进行r值引用。 r值参考最后是对值的引用。
在上面的具体情况中,关于cout语句,它相当于只获取对象的简单引用
Ptr& obj1 = list.front();
现在你将元素放入列表中,obj1 r值引用“指向”相同的数据,修改一个将导致修改
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
如果你是假的,那么
Ptr obj1 = std::move(list.front());
然后你将拥有所有权(因此列表对象的nullptr-ify)转移到obj1。
如果你按照上面的推理,你也可以意识到这一行
list.pop_front();
销毁unique_ptr对象并将r值引用保留为未定义状态。你不应该真正归还它(并使用它)。