c++ 右值引用&std::move()&移动构造函数&移动赋值函数

右值引用

所谓右值引用就是必须绑定到右值的引用,我们通过 && 而不是 & 来获得右值引用。

右值引用有一个很重要的性质--只能绑定到一个将要销毁的对象,即左值持久,右值短暂。

  int i = 42;
  int& r = i; // 正确,r 引用 i
  int&& rr = i; // 错误,不能将一个右值引用绑定到一个左值上
  int& r2 = i * 42; // 错误,i * 42 是一个右值
  const int& r3 = i * 42; // 正确,可以将一个 const 的引用绑定到一个右值上
  int&& rr2 = i * 42; // 正确,将 rr2 绑定到乘法结果上

标准库 move 函数

虽然不能将一个右值引用直接绑定到一个左值上,但我们可以显式地将一个左值转换为对应的右值引用类型。我们还可以通过调用一个名为 move 的新标准函数来获得绑定到左值的右值引用。

 int&& rr3 = std::move(rr1); // ok

move 调用告诉编译器:我们有一个左值,但我们希望像一个右值一样处理它,我们必须认识到,调用 move 就意味着:除了对 rr1 赋值或销毁它以外,我们将不再使用它,在调用 move 之后, 我们不能对移后源对象的值做任何假设。

移动构造函数&移动赋值运算符

移动构造函数和移动赋值运算符“窃取”资源,它们通常不分配任何资源。因此这两类函数通常不会抛出任何异常,当不抛出异常时必须声明为 noexcept。

  Foo(Foo&& v) noexcept { // 移动构造函数
      ; // TODO
  }
  Foo& operator = (Foo&& a) noexcept { // 移动操作运算符
      ; // TODO
      return *this;
  }

用拷贝构造函数代替移动构造函数几乎肯定是安全的,用赋值运算符代替移动赋值运算符也几乎肯定是安全的。

  Foo x;
  Foo y(x); // 拷贝构造函数,x是一个左值
  Foo z(std::move(x)); // 拷贝构造函数,因为未定义移动构造函数

在对 z 进行初始化时,我们调用了 move(x),它返回一个绑定到 x 的 Foo&&。Foo 拷贝构造函数是可行的,因为我们可以将一个 Foo&& 转换为一个 const Foo&。因此 z 的初始化将使用 Foo 的拷贝构造函数。

引用限定符

用来指出一个非 static 成员函数可以用于左值或右值的符号。限定符 & 和 && 应该放在参数列表之后或 const 之后(如果有的话)。被 & 限定的函数只能用于左值,被 && 限定的函数只能用于右值。

  Foo x;
  Foo y(x); // 拷贝构造函数,x是一个左值
  Foo z(std::move(x)); // 拷贝构造函数,因为未定义移动构造函数

代码示例

  class A {
  public:
      int val = 0;
      A() {
          std::cout << "A()" << std::endl;
      }
      A(int v) {
          std::cout << "A(int v)" << std::endl;
      }
      A& operator = (const A& a) {
          std::cout << "A& operator = (const A& a)" << std::endl;
          return *this;
      }
      A(const A& a) {
          std::cout << "A(const A& a)" << std::endl;
      }
      A& operator = (A&& a) {
          std::cout << "A& operator = (A&& a)" << std::endl;
          return *this;
      }
      A(A&& v) {
          std::cout << "A(A&& v)" << std::endl;
      }
      virtual ~A() {
          std::cout << "~A()" << std::endl;
      }
      void print(int a) && { // 如果只有此函数,c.print(3);编译报错
          std::cout << "print(int a) &&" << std::endl;
      }
      void print(int a) & { // 如果只有此函数,std::move(d).print(2);编译报错
          std::cout << "print(int a) &" << std::endl;
      }
  };
  ​
  int main()
  {
      A b; // A()
      A c; // A()
      A d; // A()
  ​
      c = b; // A& operator = (const A& a)
      d = std::move(b); // A& operator = (A&& a)
  ​
     std::move(d).print(2); // print(int a) &&
     c.print(3); // print(int a) &
// ~A()
      // ~A()
      // ~A()
      return 0;
  }

 

原文地址:https://www.cnblogs.com/tongyishu/p/13414148.html