3

可能重复:
我可以对移出的对象做什么?

例如,请参阅此代码

template<class T> 
void swap(T& a, T& b) 
{ 
    T tmp(std::move(a));
    a = std::move(b); 
    b = std::move(tmp);
} 

只是我,还是这里有错误?如果你move a进入tmp,那么不就a失效了吗?

即移动分配不应该ab一个带有放置的移动构造函数调用new吗?
如果不是,那么移动构造函数和移动赋值运算符有什么区别?

template<class T>
void swap(T& a, T& b)
{
    T tmp(std::move(a));
    new(&a) T(std::move(b));
    new(&b) T(std::move(tmp));
}
4

3 回答 3

10

当您将数据移出对象时,预期的语义是被移出的对象最终处于未指定但有效的状态。这意味着您无法预测对象将处于什么状态,除非它将是一个格式良好的对象。这与“这个对象已经死了并且消失了”不太一样。将数据移出对象并不会结束对象的生命周期——它只是将其状态更改为未指定的状态——因此为该对象分配一个新值是完全安全的。

因此,交换函数的初始版本是安全的。从 a 移动数据后,该对象拥有一些未指定的“安全但不可预测”的值。然后,在将 b 的值移动分配给它时,会覆盖该值。

第二个版本是不安全的,因为在您尝试在它之上构造一个新对象之前, a 的生命周期还没有结束。这会导致未定义的行为。

希望这可以帮助!

于 2012-07-27T16:11:12.510 回答
6

a仍然有效。但是里面的数据a将不再可靠。当您移动其资源时,您并没有解除分配a,因此将新资源移动到a.

于 2012-07-27T16:03:43.880 回答
5

移动不会使对象无效。相反,它总体上仍处于有效但不确定的状态。特定类别有额外保证;例如,std::unique_ptr保证它在被移出后将为空。

保留一个有效对象尤其意味着分配给该对象是非常好的,这就是原始代码所做的。

您自己提出的解决方案被严重破坏:当您在旧对象之上放置构建新对象时,旧对象的生命周期结束。但是,如果类的析构函数有效果,那么省略调用析构函数是未定义的行为。

此外,如果您确实首先正确调用了析构函数,然后在构造函数中遇到异常,那么您将遇到麻烦,因为您现在没有需要在范围退出时销毁的有效对象。这是我关于这个主题的一个相关问题。

于 2012-07-27T16:31:08.580 回答