[英]Mixing Rvalue and LValue reference
I'm trying to better understand LValue, RValue, and how std::move works.我试图更好地理解 LValue、RValue 以及 std::move 的工作原理。 I have the following code
我有以下代码
#include <string>
class A
{
public:
A() = default;
A(std::string&& aString): myString(std::move(aString)) {}
std::string myString;
};
class B
{
public:
void InitMembers(std::string& aString) { myA = A(std::move(aString));}
private:
A myA;
};
int main()
{
B b;
std::string hello;
b.InitMembers(hello);
}
my questions are:我的问题是:
void InitMembers(string& aString) { myA = A(std::move(aString));}
I understand that I have to use std::move to aString in order to cast aString from an LValue reference to a RValue reference. void InitMembers(string& aString) { myA = A(std::move(aString));}
我知道我必须使用 std::move to aString 才能将 aString 从 LValue 引用转换为 RValue 引用。 But I have some doubts regarding the meaning of aString in the InitMember scope. aString is provided as an LValue reference, but in the method scope it's considered as an LValue and that's why I have to use the std::move? Isn't the following implementation good as the one above?下面的实现不是和上面的一样好吗?
#include <string>
class A
{
public:
A() = default;
A(std::string& aString): myString(std::move(aString)) {}
std::string myString;
};
class B
{
public:
void InitMembers(std::string& aString) { myA = A(aString);}
private:
A myA;
};
int main()
{
B b;
std::string hello;
b.InitMembers(hello);
}
Thanks:)谢谢:)
Concerning关于
A(std::string&& aString): myString(std::move(aString)) {}
std::string&&
denotes an rvalue reference to a std::string
. std::string&&
表示对std::string
::string 的右值引用。 Rvalue references only bind to rvalues (both prvalues and xvalues), so the two possible call sites will be like this:右值引用只绑定到右值(prvalues 和 xvalues),所以两个可能的调用点将是这样的:
// assuming this is defined somewhere
std::string f(void); // returns by value, i.e. `f()` is an rvalue
// call site 1
A a1{f()}; // `f()` is an rvalue (precisely a prvalue)
// assuming this
std::string s{"ciao"}; // s is an lvalue
// call site 2
A a2{std::move(s)}; // `std::move(s)` is an rvalue (precisely an xvalue)
// i.e. with `std::move` we turn s into an rvalue argument,
// so it can bind to the rvalue reference parameter
// don't expect s to be the one it was before constructing a2
In either case, what does the constructor do with aString
?在任何一种情况下,构造函数对
aString
做了什么?
Well, aString
is an lvalue, because "it has a name" (the actual definition is a bit more complicated, but this is the easiest one to get started with, and it isn't all that wrong after all), so if you use it verbatim, the compiler won't assume it is bound to a temporary and it won't let myString
steal it resources.好吧,
aString
是一个左值,因为“它有一个名字”(实际定义有点复杂,但这是最容易上手的,而且它毕竟不是那么错误),所以如果你逐字使用它,编译器不会假定它绑定到临时对象,也不会让myString
窃取它的资源。
But you know that aString
is bound to a temporary, because you've declared it as std::string&&
, so you pass it as std::move(aString)
to tell the compiler "treat this is a temporary" .但是您知道
aString
绑定到一个临时对象,因为您已将其声明为std::string&&
,因此您将其作为std::move(aString)
传递以告诉编译器“将其视为临时对象” 。
Yes, technically also the compiler knows that aString
is bound to a temporary, but it can't std::move
it automatically.是的,从技术上讲,编译器也知道
aString
绑定到一个临时对象,但它不能自动std::move
它。 Why?为什么? Because you might want to use it more than once:
因为您可能想多次使用它:
A(std::string&& aString) : myString(aString/* can't move this */) {
std::cout << aString << std::endl; // if I want to use it here
}
// yes, this is a very silly example, sorry
As regards至于
void InitMembers(std::string& aString) { myA = A(std::move(aString));}
aString
denotes an lvalue reference to non- const
std::string
, so you can pass to .InitMembers
only non- const
lvalues. aString
表示对非const
std::string
的左值引用,因此您只能将非const
左值传递给.InitMembers
。
Then inside the function you're std::move
ing it to tell A
's constructor "look, this is a temporary" .然后在 function 中,你对它进行
std::move
以告诉A
的构造函数“看,这是一个临时的” 。 But that also means that at the call site ( b.InitMembers(hello);
) you're leaving the input ( hello
) in a moved-from state, just like the s
in the first example above.但这也意味着在调用站点 (
b.InitMembers(hello);
) 中,您将输入 ( hello
) 保留在移出的 state 中,就像上面第一个示例中的s
一样。 That's ok, because the caller knows that InitMembers
takes its parameter by non- const
lvalue reference, so it is aware that the argument they pass can be changed by the call.没关系,因为调用者知道
InitMembers
通过非const
左值引用获取其参数,因此知道它们传递的参数可以通过调用更改。 Just like in the previous example it's the user who's writing std::move
around s
, so they're supposed to know what they do.就像前面的例子一样,是用户在写
std::move
around s
,所以他们应该知道他们做了什么。
For more details about how std::move
works (and std::forward
as well), I want to point you to this answer of mine.有关
std::move
如何工作(以及std::forward
以及)的更多详细信息,我想向您指出我的这个答案。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.