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

aby*_*s.7 5 c++ rvalue-reference unique-ptr move-semantics c++11

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.
Run Code Online (Sandbox Code Playgroud)

完整来源在这里。

我不明白-为什么obj1,并list.front()仍然指向相同的位置后,std::move()被称为?

Mar*_*cia 4

里面有你参考std::unique_ptr的list。

Ptr&& obj1 = std::move(list.front());
// ^^ THIS
Run Code Online (Sandbox Code Playgroud)

所以当你这样做时

list.pop_front();
Run Code Online (Sandbox Code Playgroud)

中的唯一指针list被销毁,并且您留下了对已被销毁的某个对象的悬空引用,通过从函数返回该对象来非法使用该对象。

更新: std::move实际上并没有移动std::unique_ptr. 如果我们看看如何std::move定义,我们会发现它只返回所移动表达式的右值引用。当你测试它时,它会变得有趣:

Ptr&& obj1 = std::move(list.front());
assert(obj1.get() == list.front().get());
Run Code Online (Sandbox Code Playgroud)

不过,您的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
Run Code Online (Sandbox Code Playgroud)

@nosid 总结了我想要这个答案的含义。