函数中的r值参数

时间:2016-08-12 21:33:42

标签: c++ c++11 rvalue

我想知道在函数之间传递r值时的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-值

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

3 个答案:

答案 0 :(得分:11)

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

左值是根据§3.10.1.1:

  

左值(历史上所谓,因为左值可能出现在赋值表达式的左侧)指定一个函数或一个对象。 [示例如果 E 是指针类型的表达式,则 * E 是一个左值表达式,指向对象或函数哪个 E 。另一个例子,调用返回类型为左值引用的函数的结果是左值。 - 结束示例]

根据§3.10.1.4, rvalue

  

rvalue (历史上所谓的,因为rvalues可能出现在作业的右侧   表达式)是xvalue,临时对象(12.2)或其子对象,或与对象无关的值

基于此,str不是临时对象, 与对象关联(对象名为str),因此它不是右值。

左值的示例使用指针,但对于引用来说它是相同的,对于右值引用(它们只是一种特殊类型的引用)自然也是如此。

因此,在您的示例中,str 左值,因此您必须std::move才能调用foo(只接受rvalues而不是lvalues)。

答案 1 :(得分:7)

&#34; rvalue&#34; in&#34; rvalue reference&#34;指引用绑定到的值的类型:

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

这就是它的全部内容。重要的是,指的是使用引用时获得的值。一旦你有了一个引用变量(任何类型的引用!),id-expression命名该变量总是一个左值。 Rvalues只在临时值中出现,或者作为函数调用表达式的值,或者作为强制转换表达式的值,或者作为衰减或this的结果。

这里有一个类似的解释指针:取消引用指针始终是左值,无论指针是如何获得的:*p*(p + 1)*f()是所有的左撇子。你怎么来这件事并不重要;一旦你拥有它,它就是一个左值。

稍微退一步,也许这一切中最有趣的方面是rvalue引用是一种将rvalue转换为左值的机制。在产生可变左值的C ++ 11之前,没有这样的机制存在。虽然从一开始就将左值到右值的转换作为语言的一部分,但是发现需要进行右值到右值的转换需要更长的时间。

答案 2 :(得分:3)

  

我现在的问题是为什么?

我正在添加另一个答案,因为我想强调对“为什么”的答案。

即使名为的右值引用可以绑定到右值,但在使用时它们也会被视为左值。例如:

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&)
}

虽然右值可以绑定到a的{​​{1}}参数,但一旦绑定,f()现在被视为左值。特别是,对重载函数ag()的调用将解析为h()(左值)重载。 const A&视为a内的右值会导致容易出错的代码:首先会调用f的“移动版本”,这可能会导致{ {1}},然后被盗的g()将被发送到a的移动超载。

Reference