[英]Why doesn't std::move() of unique_ptr from list<unique_ptr> really move it?
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.
完整的來源是在這里 。
我不明白 - 為什么在調用std::move()
之后obj1
和list.front()
仍然指向同一個位置?
您有一個對 std::unique_ptr
內部list
的引用 。
Ptr&& obj1 = std::move(list.front());
// ^^ THIS
所以當你這樣做的時候
list.pop_front();
list
的唯一指針被銷毀,並且您將留下一些已經被銷毀的對象的懸空引用,這是通過從函數返回而非法使用的。
更新: std::move
實際上並沒有移動std::unique_ptr
。 如果我們看到如何定義std::move
,我們會看到它只返回正在移動的表達式的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
沒有move-semantics, std::unique_ptr
就不那么有用了。 使用移動語義, 它允許將所有權轉移到另一個對象 。
使用list.push_back(std::move(ptr));
您將數據的所有權轉移到新元素並將ptr保留為nullptr狀態( 在此處閱讀 )。
之后,如果arg為true,則由於list.front()
返回對容器中第一個元素的引用 ,因此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值引用保留為未定義狀態。 你不應該真正歸還它(並使用它)。
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.