C++11 rvalue references and std::move statement instances resolve of recommendations

  • 2020-05-17 06:00:10
  • OfStack

The rvalue reference (and its supported Move semantics and perfect forwarding) is one of the most significant language features to be added to C++0x. From a practical point of view, it can perfectly solve the long-standing problem of temporary object efficiency in C++. In terms of the language itself, it addresses the deficiencies of the reference types in C++ in terms of left and right values. From the point of view of the library designer, it brings another tool to the library designer. From the point of view of library users, you can get a "free" efficiency increase without moving a pawn...

Let's take a closer look at rvalued references with an example.

1. What is left value and what is right value? Simply speaking, left value can be assigned, and right value cannot be assigned. For example, "A a = getA();" In this statement, a is the left value and the return value of getA() is the right value.


#include "stdafx.h"
#include <iostream>
class A
{
public:
  A() { std::cout << "Constructor" << std::endl; }
  A(const A&) { std::cout << "Copy Constructor" << std::endl; }
  ~A() {}
};
static A getA()
{
  A a;
  return a;
}
int main()
{
  A a = getA();
  return 0;
}

Run the above code and the output results are as follows:

Constructor
Copy Constructor

You can see that the constructor of A is called once, and the copy constructor is called once. The constructor and the copy constructor are relatively expensive. Is it possible to avoid the copy constructor here? C++11 does that.

2. Add the mobile constructor of A, the code is as follows:


#include "stdafx.h"
#include <iostream>
class A
{
public:
  A() { std::cout << "Constructor" << std::endl; }
  A(const A&) { std::cout << "Copy Constructor" << std::endl; }
  A(const A&&) { std::cout << "Move Constructor" << std::endl; }
  ~A() {}
};
static A getA()
{
  A a;
  return a;
}
int main()
{
  A a = getA();
  return 0;
}

Run the above code and output the result:

Constructor
Move Constructor

Instead of calling the copy constructor, call the move constructor. You don't see the advantages of moving structures here.

3. Modify the code to add a member variable to A class as follows:


#include "stdafx.h"
#include <iostream>
#include <vector>
class B
{
public:
  B() {}
  B(const B&) { std::cout << "B Constructor" << std::endl; }
};
class A
{
public:
  A(): m_b(new B()) { std::cout << "A Constructor" << std::endl; }
  A(const A& src) :
    m_b(new B(*(src.m_b)))
  { 
    std::cout << "A Copy Constructor" << std::endl;
  }
  A(A&& src) :
    m_b(src.m_b)
  {
    src.m_b = nullptr;
    std::cout << "A Move Constructor" << std::endl;
  }
  ~A() { delete m_b; }
private:
  B* m_b;
};
static A getA()
{
  A a;
  std::cout << "================================================" << std::endl;
  return a;
}
int main()
{
  A a = getA();
  std::cout << "================================================" << std::endl;
  A a1(a);
  return 0;
}

Run the above code and output the result:

A Constructor
================================================
A Move Constructor
================================================
B Constructor
A Copy Constructor

"A a = getA ();" The mobile construct of A is called, "A a1(a);" The copy construct of A is called. The copy construction of A requires a deep copy of the member variable B, while the mobile construction of A does not. Obviously, the mobile construction of A is efficient.

4.std::move statement can change the left value to the right value to avoid copy construction, modify the code as follows:


#include "stdafx.h"
#include <iostream>
#include <vector>
class B
{
public:
  B() {}
  B(const B&) { std::cout << "B Constructor" << std::endl; }
};
class A
{
public:
  A(): m_b(new B()) { std::cout << "A Constructor" << std::endl; }
  A(const A& src) :
    m_b(new B(*(src.m_b)))
  { 
    std::cout << "A Copy Constructor" << std::endl;
  }
  A(A&& src) :
    m_b(src.m_b)
  {
    src.m_b = nullptr;
    std::cout << "A Move Constructor" << std::endl;
  }
  ~A() { delete m_b; }
private:
  B* m_b;
};
static A getA()
{
  A a;
  std::cout << "================================================" << std::endl;
  return a;
}
int main()
{
  A a = getA();
  std::cout << "================================================" << std::endl;
  A a1(a);
  std::cout << "================================================" << std::endl;
  A a2(std::move(a1));
  return 0;
}

Run the above code and output the result:

A Constructor
================================================
A Move Constructor
================================================
B Constructor
A Copy Constructor
================================================
A Move Constructor

"A a2 (std: : move (a1));" Convert a1 to an rvalue, so a2 calls a move construct instead of a copy construct.

5. The assignment operator can also be a move assignment.


#include "stdafx.h"
#include <iostream>
#include <vector>
class B
{
public:
  B() {}
  B(const B&) { std::cout << "B Constructor" << std::endl; }
};
class A
{
public:
  A(): m_b(new B()) { std::cout << "A Constructor" << std::endl; }
  A(const A& src) :
    m_b(new B(*(src.m_b)))
  { 
    std::cout << "A Copy Constructor" << std::endl;
  }
  A(A&& src) :
    m_b(src.m_b)
  {
    src.m_b = nullptr;
    std::cout << "A Move Constructor" << std::endl;
  }
  A& operator=(const A& src)
  {
    if (this == &src)
      return *this;
    m_b = new B(*(src.m_b));
    std::cout << "operator=(const A& src)" << std::endl;
    return *this;
  }
  A& operator=(A&& src)
  {
    if (this == &src)
      return *this;
    m_b = src.m_b;
    src.m_b = nullptr;
    std::cout << "operator=(const A&& src)" << std::endl;
    return *this;
  }
  ~A() { delete m_b; }
private:
  B* m_b;
};
static A getA()
{
  A a;
  std::cout << "================================================" << std::endl;
  return a;
}
int main()
{
  A a = getA();// Mobile structure 
  std::cout << "================================================" << std::endl;
  A a1(a);// Copy the structure 
  std::cout << "================================================" << std::endl;
  A a2(std::move(a1));// Mobile structure 
  std::cout << "================================================" << std::endl;
  a2 = getA();// Mobile assignment 
  std::cout << "================================================" << std::endl;
  a2 = a1;// Copy assignment 
  return 0;
}

Run the above code and output the result:

A Constructor
================================================
A Move Constructor
================================================
B Constructor
A Copy Constructor
================================================
A Move Constructor
================================================
A Constructor
================================================
A Move Constructor
operator=(const A&& src)
================================================
B Constructor
operator=(const A& src)

In general, try to add move constructs and move assignment functions to the class and reduce the consumption of copy constructs and copy assignments.


Related articles: