1

我试图更好地理解 LValue、RValue 以及 std::move 的工作原理。我有以下代码

#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);
}

我的问题是:

  • void InitMembers(string& aString) { myA = A(std::move(aString));}我所知,我必须使用 std::move 到 aString 才能将 aString 从 LValue 引用转换为 RValue 引用。但是我对 InitMember 范围内的 aString 的含义有些怀疑。aString 作为 LValue 引用提供,但在方法范围内它被视为 LValue,这就是为什么我必须使用 std::move? Std::move 应该依赖于引用推导(对吗?),在这种情况下它如何推导类型?它会推导出一个类型“字符串”或“字符串&”,因为 aString 是作为方法参数中的 LValue 引用提供的?
  • 为什么我还必须在 A 的构造函数初始化程序中使用 std::move?aString 是触发移动构造函数的 RValue 引用这一事实还不够吗?

下面的实现不是和上面的一样好吗?

#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);
} 

谢谢 :)

4

1 回答 1

1

关于

A(std::string&& aString): myString(std::move(aString)) {}

std::string&&表示对 a 的右值引用std::string。右值引用只绑定到右值(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

在任何一种情况下,构造函数都做了aString什么?

好吧,aString是一个左值,因为“它有一个名字”(实际的定义有点复杂,但这是最容易上手的,而且它毕竟不是错的),所以如果你使用逐字逐句地,编译器不会假设它绑定到一个临时的,也不会让它myString窃取它的资源。

但是知道aString绑定到一个临时的,因为你已经将它声明为std::string&&,所以你将它作为std::move(aString)告诉编译器“对待这是一个临时的”

是的,从技术上讲,编译器也知道aString绑定到一个临时的,但它不能std::move自动。为什么?因为您可能想多次使用它:

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

至于

void InitMembers(std::string& aString) { myA = A(std::move(aString));}

aString表示对非-的左值引用const std::string,因此您只能传递给.InitMembers非-左值const

然后在函数内部std::move告诉A构造函数“看,这是一个临时的”。但这也意味着在调用站点 ( b.InitMembers(hello);) 您将输入 ( hello) 留在移出状态,就像s上面第一个示例中的一样。没关系,因为调用者知道通过InitMembers左值引用获取其参数,所以它知道它们传递的参数可以被调用更改。就像在前面的示例中一样,是用户在周围写东西,所以他们应该知道自己在做什么。conststd::moves

有关如何std::move工作(std::forward以及)的更多详细信息,我想向您指出我的这个答案

于 2021-10-12T08:31:08.923 回答