Hon*_*hen 4 c++ move rvalue-reference c++11
就像std::move(v)演员表一样
static_cast<T&&>(v)
Run Code Online (Sandbox Code Playgroud)
T的类型在哪里v?但是为什么移动的变量不是reference原始对象的 a 呢?
例如,当o1被“感动”来o2,我们为什么不能访问str_的o2通过o1.str_了吗?
#include <string>
#include <iostream>
struct MyClass {
std::string str_;
MyClass(MyClass const &) = delete;
MyClass &operator=(MyClass const &) = delete;
MyClass(MyClass &&o) : str_(std::move(o.str_)) {}
MyClass(std::string const &str) : str_(str) {}
};
int main(void) {
MyClass o1 = MyClass("o1");
MyClass o2(std::move(o1));
std::cout << "o1: " << o1.str_ << "\n"
<< "o2: " << o2.str_ << std::endl;
return 0;
}
Run Code Online (Sandbox Code Playgroud)
输出:
o1:
o2: o1
Run Code Online (Sandbox Code Playgroud)
似乎当我改变
MyClass(MyClass &&o) : str_(std::move(o.str_)) {}
Run Code Online (Sandbox Code Playgroud)
到
MyClass(MyClass &&o) : str_(o.str_) {}
Run Code Online (Sandbox Code Playgroud)
输出将是:
o1: o1
o2: o1
Run Code Online (Sandbox Code Playgroud)
所以根本原因是“std::string”的举动?但为什么这会有所不同?
让我们暂时忘记您的课程,仅以std::string一个为例。
std::string s1{"Hello, World!"}; // 1
std::string s2{s1}; // 2
std::string s3{std::move(s1)}; // 3
Run Code Online (Sandbox Code Playgroud)
在第 1 行,您已经构造了一个std::string对象。在第 2 行,您要复制 s1到s2. 现在,s1和s2都将包含自己的字符串副本"Hello, World!"。这个复制是由std::string(or std::basic_string<char>)的复制构造函数完成的,它不修改参数。
basic_string(basic_string const& other);
Run Code Online (Sandbox Code Playgroud)
在第 3 行,您将内容移动s1到s3。为了做到这一点,你首先投射s1到std::string&&(这是做什么的std::move)。由于这个转换,调用现在将匹配std::string的移动构造函数,而不是像前一行那样的复制构造函数。
basic_string(basic_string&& other) noexcept;
Run Code Online (Sandbox Code Playgroud)
这个构造函数,因为它总是用字符串的右值实例调用,有从参数中窃取资源的许可。因此,内部构造会简单地对一些指针复制到被分配的内存†通过s1存储字符串,并设置状态s1实例,使得它现在是空的。因此s3现在拥有字符串。
当您string在类实例中移动数据成员时,也会发生同样的事情。这就是为什么在std::string打印时移动的对象显示为空的原因。
†如果std::string实现使用小字符串优化,则执行的操作会有所不同,但这只是一个实现细节。从概念上讲,这两种情况都按上述方式工作。
你说得对,std::move只是将其参数转换为右值引用。
但是任何接收这样一个右值引用的函数都有明确的许可来掠夺它以更有效地完成它的工作,它只需要让它处于某种任意的有效状态。
不过,您当然可以继续使用它,但请注意“某些任意有效状态”的保证是多么少。
这种对移动对象的掠夺使移动语义移动语义,并且是明确的目标。