函数中的右值参数

r-value parameters in a function

我想知道在函数之间传递右值时的 C++ 行为。

看看这个简单的代码:

#include <string>

void foo(std::string&& str) {
  // Accept a rvalue of str
}

void bar(std::string&& str) {
  // foo(str);          // Does not compile. Compiler says cannot bind lvalue into rvalue.
  foo(std::move(str));  // It feels like a re-casting into a r-value?
}

int main(int argc, char *argv[]) {
  bar(std::string("c++_rvalue"));
  return 0;
}

我知道当我在 bar 函数中时,我需要使用 move 函数才能调用 foo 函数。我现在的问题是为什么?

当我在 bar 函数中时,变量 str 应该已经是一个 r-value,但编译器的行为就像它是一个l-值.

有人可以引用一些关于此行为的标准参考吗? 谢谢!

"rvalue reference"中的"rvalue"是指引用可以绑定到的那种值:

  • 左值引用可以绑定到左值
  • 右值引用可以绑定到右值
  • (+一点)

仅此而已。重要的是,它 而不是 引用当您 使用 引用时获得的值。一旦你有了一个引用变量(任何类型的引用!),命名该变量的 id 表达式总是一个左值。右值仅作为临时值、函数调用表达式的值、强制转换表达式的值、衰减或 this.

的结果出现在野外。

这里与取消引用指针有一定的类比:取消引用指针始终是左值,无论该指针是如何获得的:*p*(p + 1)*f() 都是左值。你是怎么得到这东西的并不重要;一旦你拥有它,它就是一个左值。

退一步说,也许所有这一切中最有趣的方面是右值引用是一种将右值转换为左值的机制。在 C++11 之前不存在这样的产生可变左值的机制。虽然左值到右值的转换从一开始就是该语言的一部分,但发现右值到左值转换的必要性却花了更长的时间。

str 是一个右值引用,即 它只是对右值的引用。但它仍然是一个引用,是一个左值。您可以使用 str 作为变量,这也意味着它是一个左值,而不是临时右值。

一个 lvalue 根据 §3.10.1.1 是:

An lvalue (so called, historically, because lvalues could appear on the left-hand side of an assignment expression) designates a function or an object. [ Example: If E is an expression of pointer type, then *E is an lvalue expression referring to the object or function to which E points. As another example, the result of calling a function whose return type is an lvalue reference is an lvalue. —end example ]

并且 rvalue 根据 §3.10.1.4 是:

An rvalue (so called, historically, because rvalues could appear on the right-hand side of an assignment expression) is an xvalue, a temporary object (12.2) or subobject thereof, or a value that is not associated with an object.

基于此,str不是一个临时对象,它关联到一个对象(对象名为str),并且所以它不是右值。

左值的示例使用指针,但对于引用也是一样,自然对于右值引用(这只是一种特殊类型的引用)也是如此。

所以,在你的例子中,str一个左值,所以你必须std::move它调用 foo(只接受右值,不接受左值)。

My question now is why?

我正在添加另一个答案,因为我想强调 "why" 的答案。

尽管 named 右值引用可以绑定到右值,但在使用时它们被视为左值。例如:

struct A {};

void h(const A&);
void h(A&&);

void g(const A&);
void g(A&&);

void f(A&& a)
{
    g(a);  // calls g(const A&)
    h(a);  // calls h(const A&)
}

虽然右值可以绑定到 f()a 参数,但一旦绑定,a 现在就被视为左值。特别是,对重载函数 g()h() 的调用解析为 const A&(左值)重载。 a 视为 f 中的右值会导致容易出错的代码: 首先会调用 g() 的 "move version",这可能会窃取 a,然后被窃取的 a 将被发送到 h().

的移动重载

Reference.