如果真的需要std::move,我们应该什么时候声明右值refs



正如许多其他帖子所解释的那样,将一个变量声明为右值引用并不能保证它将调用移动赋值运算符,只有使用std::move(即转换为同一右值引用(才能做到这一点。例如:

struct A
{
A& operator=(A&& l_other) { std::cout << "move" << std::endl; }
A& operator=(A&  l_other) { std::cout << "copy" << std::endl; }
};
A a;
// does not print anything, compiler does nothing here!
A&& b = std::move(a);
// prints "copy", not "move"
a = b;
// prints "move", but needs std::move()
a = std::move(b);

似乎将b定义为A&&在那一刻不会移动任何东西。之后,a = b不会自动移动b,即使右侧部分被定义为A&&,正如其他帖子所解释的那样。我们需要显式调用std::move(b)来移动b

我的问题是,将变量定义为A&&有什么用处我可以完美地将其定义为A&,并完全相同地移动它。

我的问题是,将变量定义为a&amp;?我完全可以把它定义为A&并以完全相同的方式移动它。

如果在没有警告的情况下将命名变量传递到函数中并进行类似的修改,这将非常令人不快,并可能导致令人不快的调试会话。

当您通过右值引用时,类型系统强制执行有用的保证。要么引用绑定到一个右值,因此修改它不会违反任何人的假设(无论如何,它很快就会消失(。或者您被授予了移出它的显式权限。调用上下文不能意外地授予您修改左值的权限,必须使用显式强制转换(std::move(。

显式比隐式好。让右值引用以它们的方式与类型系统交互有助于确保移动不会使对象处于意外状态。浏览一下调用上下文就会发现,传递到函数中的变量可能会处于未知状态,因为它被对std::move的调用显式包围。

这就是右值引用的好处。

这样想:涉及到两个方面:对象的生产者(将被引用绑定(和引用的消费者。

引用的类型定义了允许进入引用(生产者(的内容,而不是变量(消费者(中是什么。

对于消费者来说,它是哪种参考没有区别。

对于生产者来说,右值引用是一个只能绑定消耗性值的符号(临时值或x值,如str::move的结果(。

最新更新