如果他们确实需要std::move,我们什么时候应该声明右值引用?

When should we declare rvalue refs if they actually need std::move?

正如许多其他帖子所解释的那样,将一个变量声明为右值引用 而不是 保证它将调用移动赋值运算符,仅使用 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& 并完全相同地移动它.

My question is, what's the utility of defining a variable as A&&? I could perfectly define it as A& and move it exactly the same.

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

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

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

调用包围

这就是右值引用的好处。

可以这样想:涉及两方:对象的生产者(受引用绑定)和引用的使用者。

引用的种类定义允许进入引用(生产者)的内容,而不是变量(消费者)中的内容).

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

对于生产者来说,右值引用是一个标志,表明只能绑定一个可消耗值(一个临时值或一个 xvalue,例如 str::move 的结果)。