在c ++ 11中为什么不能在std :: move之后使用移动变量?

时间:2014-08-16 15:01:35

标签: c++ c++11 move rvalue-reference

因为std::move(v)只是一个类似

的演员
static_cast<T&&>(v)

其中Tv的类型。但为什么移动变量不是原始对象的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”移动?但为什么这会有所不同?

3 个答案:

答案 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。现在,s1s2都将包含自己的字符串"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)

因为大多数移动构造函数的实现确实将数据从一个对象移动到另一个对象并使第一个对象上的数据无效。

这就是为什么它被称为移动而不是复制。