例如,请参阅this code:
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
会无效吗?
即。来自a
的{{1}}移动分配不应该是位置b
的移动构造函数调用吗?
如果没有,那么移动构造函数和移动赋值运算符之间有什么区别?
new
答案 0 :(得分:9)
当您从对象移出数据时,预期的语义是移动的对象最终处于未指定但有效的状态。这意味着你无法预测对象将处于什么状态,除了它将是一个格式良好的对象。这与“这个物体已经死了,不见了”并不完全相同。将数据移出对象并不会终止对象的生命周期 - 它只是将其状态更改为未指定的状态 - 因此为该对象分配新值是完全安全的。
因此,交换功能的初始版本是安全的。从a移动数据后,该对象保留了一些未指定的“安全但不可预测”的值。移动时,该值将被覆盖,并将其赋值为b。
第二个版本不安全,因为在您尝试在其上构建新对象之前,a的生命周期尚未结束。这导致了不确定的行为。
希望这有帮助!
答案 1 :(得分:6)
a
仍然有效。 a
内的数据不再可靠。
移动资源时,您不会取消分配a
,因此将新资源移至a
完全可以。
答案 2 :(得分:5)
移动不会使对象无效。相反,它总体上仍处于有效但不确定的状态。特定班级有额外保证;例如,std::unique_ptr
保证从移动后它将为null。
保留有效对象尤其意味着分配给对象是完全正确的,这就是原始代码的作用。
您自己提出的解决方案严重受损:当您在旧对象上放置构造新对象时,旧对象的生命周期结束。但是,如果类的析构函数具有效果,则省略调用析构函数是未定义的行为。
此外,如果你首先正确地调用析构函数,但在构造函数中遇到异常,那么你就会遇到麻烦,因为你现在没有一个需要的有效对象在范围出口处被摧毁。这是关于此主题的related question of mine。