The specific use of C++11 rvalue references

  • 2020-07-21 09:24:24
  • OfStack

C++11 introduces std::move semantics, right-value references, move constructs, and perfect forwarding. Due to the length of this part, it is divided into 3 chapters.

Before we understand these features, let's introduce a few questions.

1. Problem import

How many object constructs and how many copies does the function return? The parameter of the function is how many times does the object construct occur when the value is passed?

Let's look at one piece of code,


// main.cpp
#include <iostream>
using namespace std;

class A{
public:
  A(){
   cout<<"class A construct!"<<endl;
  } 
  A(const A&){
   cout<<"class A copy!"<<endl;
  }
  A& operator=(const A&){
   cout<<"assignment called!"<<endl;
  }
  ~A(){
   cout<<"class A destruct!"<<endl;
  }
};

A get_A_value(){
  return A();
}
int main(){
  A a = get_A_value();
  return 0;
}

Compile with g++; Note the use of -ES15en-ES16en-ES17en to close elliptic construction optimization


g++ main.cpp -fno-elide-constructors

You get the following output

[

class A construct!
class A destruct!
class A copy!
class A destruct!
class A destruct!

]

You can see that A a=get_A_value(); 1 line of code generates 1 object construct and 2 object copy constructs! Specific for

In get_A_value(), A() constructed the temporary object and the construction occurred once. When the function returns, the temporary object will be copied as the return value. A a = The function return value has been copied.

If you use compiler optimization (the default), you omit the copy of the temporary object and the copy of the final object constructed with the return value. That is, there is only one copy and destruction.

[

class A construct!
class A destruct!

]

If I change this code by 1


// ... A

void pass_A_by_value(A a){

}
int main(){
  A a;
  pass_A_by_value(a);
  return 0;
}

When optimizing g++ ES64en. cpp-ES66en-ES67en-ES68en is removed, the output is

[

class A construct!
class A copy!
class A destruct!
class A destruct!

]

1 construct plus 1 copy.

So the next time someone asks you this question in an interview, you can say: By default, the copy of the temporary object is left out when the compiler optimizes, and if you use -fno-ES86en-ES87en to omit the optimization, then consider the copy of the temporary object.

In fact, without optimization, the copy constructor is called when:

A copy occurs when a local object inside a function is returned as a return value (not a reference). Copy construction occurs when a function parameter is passed When an object is initialized with another object

Frequent object construction is a program overhead, especially when the object has heap memory (such as new members), each copy construction needs to use new to apply a block of memory, causing performance degradation. For case 2, it is a good practice to pass references if the function argument is read-only (i.e., not modified within the program), which is ES94en_ES95en_by_A (const A) & a); In case 1, the compiler will optimize for us; For case 3, C++11 introduces the concept of a move constructor, which takes a ** rvalue reference *, rvalue "resource" move to a new object without claiming new memory, thus improving efficiency and performance.

So, to understand the keywords "movement construct" and "movement semantics", you first need to understand rvalues and rvalues references.

2. Rvalue and rvalue reference

2.1 Left value (lvalue) and right value (rvalue)

In the 1. Problem import, we mentioned temporary objects, that is, objects that will only "temporarily" exist when the function returns a value (running more than 1 row will end its lifetime). This temporary return value is 1 rvalue;
The most intuitive definition of right value is, as the name implies:

The value to the right of assignment operator =, rvalue; The one on the left is left

Such as


A a = foo(); // foo()  For the right value 
char *x = "thu"; //  " thu "Is both the literal value and the rvalue 
a = b + c; // b + c And this result is also 1 A right value 

In C++, another definition is:

The left value can obtain the address and have a name; Cannot get address, no name is rvalue.

So A a = foo() works & a gets the address of a, a is the left value, but cannot get the address of foo(), ( & foo()) did not compile, and the temporary object returned by foo() has no name, so it is rvalue.

In C++11, there are two types of right values: one is the dying value (xvalue, eXpiring Value), and the other is the pure right value (prvalue,Pure Rvalue)[1]. A temporary object returned by a function without a reference, the result of an operand expression, literals such as 1, 3.14,'c', etc., are pure right values. xvalue is introduced by C++11 if the return value is A & & The return value of std::move(), etc.

Without going into details, we just need to know the difference between left and right values. The detailed classification of rvalues need not be explored.

2.2 Lvalue references and rvalue references

An lvalue reference is a reference like a 1, and a 1 is used & Represent, for example


const A &a_ref = a; //  Obtains the object  a  A reference to the 

An lvalue reference is an alias that refers to a specific object.

Rvalue references

An rvalue reference, as the name implies, is an rvalue reference & & Said.
A & & r_ref = getRvalue (); // r_ref is a rvalue reference
A rvalue reference is also an alias, distinguished from an lvalue by the fact that a rvalue reference is an alias for an unnamed variable.

getRvalue() is a function that returns an rvalue that ends its lifetime after execution. But the == rvalue reference forces it to persist ==; Use an rvalue reference to point to an rvalue. The lifetime of an rvalue and the length of an rvalue reference 1 are longer, so there is one less object destruction and construction.

The rvalue reference of C++ is mainly used for two purposes, one is mobile semantics, the other is perfect forwarding. This will be covered in the next two installments.

conclusion

In order to import right value and move semantics, we first review how many times the following temporary objects are constructed when a function returns a value and passes a parameter. It then compares left and right values, as well as the form and meaning of right value references. Pave the way for the introduction of mobile semantics and perfect forwarding.

The resources

Michale Wang| IBM XL compiler, China, in-depth understanding of C++11, machinery industry press


Related articles: