简体   繁体   English

混合右值和左值引用

[英]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:我的问题是:

  • In 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. In 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?但是我对 InitMember scope 中 aString 的含义有一些疑问。aString 作为 LValue 参考提供,但在方法 scope 中它被视为 LValue,这就是为什么我必须使用 std::move? Std::move should rely on reference deduction (right?), how does it deduce the type in this case? Std::move 应该依赖于引用推导(对吗?),在这种情况下它是如何推导类型的? It will deduce a type "string" o "string&" since aString is provided as a LValue Reference in the method's arguments?它将推导出类型“string”或“string&”,因为 aString 在方法的 arguments 中作为 LValue Reference 提供?
  • Why do I have to use std::move also in the constructor initializer of A?为什么我还必须在 A 的构造函数初始值设定项中使用 std::move? Shouldn't be enough the fact that aString is an RValue reference, which triggers the move constructor? aString 是一个触发移动构造函数的 RValue 引用这一事实还不够吗?

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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM