因为std::move(v)
只是一个类似
static_cast<T&&>(v)
其中T
是v
的类型。但为什么移动变量不是原始对象的reference
?
例如,当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;
}
输出:
o1:
o2: o1
似乎我改变了
MyClass(MyClass &&o) : str_(std::move(o.str_)) {}
到
MyClass(MyClass &&o) : str_(o.str_) {}
输出将是:
o1: o1
o2: o1
所以根本原因是“std :: string”移动?但为什么这会有所不同?
答案 0 :(得分:4)
std::move
只是将其参数转换为rvalue-reference。
但是接收这样一个rvalue-reference的任何函数都有明确的许可来掠夺它以便更有效地完成它的工作,它只是必须让它处于某种任意的有效状态。
你当然可以继续使用它,只要知道“某些任意有效状态”保证的程度如何。
移动对象的这种掠夺使得移动语义移动语义成为明确的目标。
答案 1 :(得分:3)
让我们暂时忘掉你的课程,并以std::string
为例。
std::string s1{"Hello, World!"}; // 1
std::string s2{s1}; // 2
std::string s3{std::move(s1)}; // 3
在第1行,您构建了一个std::string
对象。在第2行,您将 s1
复制到s2
。现在,s1
和s2
都将包含自己的字符串"Hello, World!"
副本。此副本由std::string
(或std::basic_string<char>
)的复制构造函数完成,该复制构造函数不会修改参数。
basic_string(basic_string const& other);
在第3行,您移动 s1
的内容到s3
。为此,您首先将s1
投射到std::string&&
(这是std::move
所做的)。由于这个强制转换,调用现在将匹配std::string
的移动构造函数,而不是像上一行那样的复制构造函数。
basic_string(basic_string&& other) noexcept;
此构造函数因为始终使用字符串的rvalue实例调用,因此具有从参数中窃取资源的许可。因此,内部构造函数将简单地复制一些指向由s1
分配†的内存的指针来存储字符串,并设置s1
实例的状态以便它现在是空的。因此s3
现在拥有字符串。
当您在类实例中移动string
数据成员时,会发生同样的事情。这就是为什么从std::string
移动的对象在打印时显示为空的原因。
†如果std::string
实现使用小字符串优化,则执行的操作会有所不同,但这只是一个实现细节。从概念上讲,两种情况都如上所述。
答案 2 :(得分:0)
因为大多数移动构造函数的实现确实将数据从一个对象移动到另一个对象并使第一个对象上的数据无效。
这就是为什么它被称为移动而不是复制。