move表示可以重用对象中的资源 move实质是将任何值强制转换为「右值」引用,使得「移动构造函数」可以匹配上。move需要和「移动构造函数/移动赋值运算符」一起使用才可以起到减少拷贝的作用。

move

move(a)的含义是:a中的资源可以移动到其他对象中,被再利用。
例如:

  1. vector<string> words;
  2. string str = "123";
  3. words.push_back(move(str));
  4. cout << "after move: " << str;
  5. // "after move: "

move的原理:实质上是将类型强制转换为「右值引用 T&&」类型。

move需要配合「移动构造函数」或「移动赋值函数」一起使用:

  1. struct Obj {
  2. // 移动构造函数
  3. Obj(Obj&& obj) {
  4. member = move(obj.member); // 再调用成员的移动构造函数
  5. }
  6. // 移动赋值运算
  7. Obj& operator=(Obj&&) {
  8. member = move(obj.member);
  9. return *this;
  10. }
  11. };

构造函数的类型

构造函数的类型:

  1. // 默认构造函数
  2. T() {}
  3. // 复制构造函数
  4. T(const T& t) {}
  5. // 移动构造函数
  6. T(T&& t) {}

左值和右值

C++11开始,表达式可以按照两个维度进行划分:「是否拥有身份」和「能否被移动」。三种基本的表达式类型「亡值 xvalue」,「左值 lvalue」和「纯右值prvalue」如下所示:

拥有身份
(泛左值)
不拥有身份
可被移动
(右值)
亡值
xvalue
纯右值
prvalue
不可被移动 左值
lvalue
-

其中,「亡值」和「左值」合起来是「泛左值」;「亡值」和「纯右值」合起来是「右值」。

基本类型

  1. 左值
    • 特点是:可以取地址;
    • 左值可用于初始化「左值引用T&」,此时相当于为表达式对象关联新的名字。
    • 常见的左值:字符串字面量,变量名称构成的表达式,++a,等
  2. 纯右值
    • 常见的纯右值:除字符串字面量以外的字面量,如 42, true, nullptr
    • 注:a++是纯右值,而 ++a是左值。因此 ++a性能更高。
  3. 亡值
    • std::move(x)是亡值。

注:右值:可被移动的值是右值,「纯右值」和「亡值」都是右值。

  • 右值可用于初始化「常量左值引用 const T&」,此时对象的生命周期延长至作用域结束;
  • 右值可用于初始化「右值引用 T&&」,此时对象的生命周期延长至作用域结束。

例子:字符串常量作为左值:

  1. cout << &"123" << endl; // 0x100c17e90
  2. auto & str = "123";
  3. cout << &str << endl; // 0x100c17e90

例子:变量的表达式是左值:

  1. struct A {
  2. // 移动构造函数
  3. A(A&& a) { ... }
  4. };
  5. void foo(A&& a) {
  6. // 变量 a 是 A&& 类型的,但是
  7. // 「变量的表达式」是「左值表达式」,需要使用 move 转换为右值
  8. // 这样才能使用「右值构造函数」
  9. A a2 = std::move(a);
  10. }