我想知道在函数之间传递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-值
有人可以引用一些有关此行为的标准吗? 谢谢!
答案 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;指引用绑定到的值的类型:
这就是它的全部内容。重要的是,不指的是使用引用时获得的值。一旦你有了一个引用变量(任何类型的引用!),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()
现在被视为左值。特别是,对重载函数a
和g()
的调用将解析为h()
(左值)重载。 将const A&
视为a
内的右值会导致容易出错的代码:首先会调用f
的“移动版本”,这可能会导致{ {1}},然后被盗的g()
将被发送到a
的移动超载。