为什么list <unique_ptr>中的unique_ptr的std :: move()没有真正移动它?</unique_ptr>

时间:2014-03-01 09:01:30

标签: c++ c++11 move-semantics unique-ptr rvalue-reference

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

我不明白 - 为什么obj1list.front()在调用std::move()后仍指向同一位置?

2 个答案:

答案 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

@nosid has summed-up what I want this answer to mean

答案 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 containerstd::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值引用保留为未定义状态。你不应该真正归还它(并使用它)。