C++11 rvalue Reference and transfer semantics

Source: Internet
Author: User

Rvalue reference (Rvalue referene) is a new feature introduced in the new C + + standard (c++11, 11 for 2011), which implements transfer semantics (move sementics) and precise delivery (Perfect Forwarding). Its main purpose is in two areas:

    1. Eliminate unnecessary copying of objects in two object interactions, save storage resources and improve efficiency.
    2. The ability to define generic functions more succinctly and explicitly.

Definition of Lvalue vs. right value

All expressions and variables in C + + (including c) are either Lvalue or rvalue. The definition of a popular lvalue is a non-temporary object, which can be used in multiple statements. All variables satisfy this definition and can be used in multiple codes, all of which are lvalue values. The right values refer to temporary objects, which are valid only in the current statement. Take a look at the following examples:

    1. A simple assignment statement
      such as: int i = 0;

      In this statement, I is an lvalue, 0 is a temporary value, or the right value. In the following code, I can be referenced, 0 is not possible. The immediate number is the right value.

    2. The right value can also appear to the left of an assignment expression, but it cannot be an assigned object because the right value is only valid in the current statement and the assignment is meaningless.

      Such as:((i>0) ? i : j) = 1;

      In this example, 0 appears on the left side of the "=" as the right value. But the assignment object is I or J, both are lvalue values.

      Before c++11, the right value cannot be referenced, and the maximum is to bind a right value with a constant reference, such as:

      const int &a = 1;

      In this case, the right value cannot be modified. But actually the right value can be modified, such as:

      T (). Set (). get ();

      T is a class, set is a function that assigns a value to a variable in T, and get is used to remove the value of the variable. In this sentence, T () generates a temporary object, which is the right value, and set () modifies the value of the variable and modifies the right value.

      Since the right value can be modified, an rvalue reference can be implemented. Rvalue references make it easy to solve problems in real-world projects and achieve attractive solutions.

Syntax symbols for left and right values

The declaration symbol for the lvalue is "&", and for the left value, the declaration symbol for the right value is "&&".

Sample program:

void Process_value (int& i) {   std::cout << "LValue processed:" << i << Std::endl;  }  void Process_value (int&& i) {   std::cout << "RValue processed:" << i << Std::endl;  }  int main () {   int a = 0;   Process_value (a);   Process_value (1);  }

Operation Result:

LValue processed:0  RValue processed:1

The Process_value function is overloaded, accepting both lvalue and rvalue values, respectively. As you can see from the output, the temporary objects are treated as rvalue values.

However, if a temporary object is passed to another function through a function that accepts an rvalue, it becomes an lvalue, because the temporary object becomes a named object during the transfer process.

Sample program:

void Process_value (int& i) {   std::cout << "LValue processed:" << i << Std::endl;  }  void Process_value (int&& i) {   std::cout << "RValue processed:" << i << Std::endl;  }  void Forward_value (int&& i) {   process_value (i);  }  int main () {   int a = 0;   Process_value (a);   Process_value (1);   Forward_value (2);  }

Operation Result:

LValue processed:0  RValue processed:1  LValue processed:2

Although 2 this immediate number is the right value when the function Forward_value is received, it becomes an lvalue when Process_value receives it.

Definition of transfer semantics

Rvalue references are used to support the transfer semantics. Transfer semantics can move resources (heaps, system objects, and so on) from one object to another, reducing the creation, copying, and destruction of unnecessary temporary objects, which can significantly improve the performance of C + + applications. Maintenance (creation and destruction) of temporary objects has a serious impact on performance.

The transfer semantics are relative to the copy semantics and can be analogous to the cut and copy of a file, and when we copy a file from one directory to another, the speed is much slower than the cut.

By transferring semantics, the resources in the temporary object can be transferred to other objects.

In the existing C + + mechanism, we can define copy constructors and assignment functions. To implement transfer semantics, you need to define a transfer constructor, and you can also define a transfer assignment operator. The transfer constructor and transfer assignment operators are called for copy and assignment of the right value. If the transfer constructor and the transfer copy operator are not defined, then the existing mechanism is followed, and the copy constructor and assignment operator are called.

Normal functions and operators can also use rvalue reference operators to implement the transfer semantics.

Implementing transfer constructors and transfer assignment functions

The copy constructor and copy assignment operators are implemented as examples of a simple string class.

Sample program:

 class MyString {private:char* _data;   size_t _len;     void _init_data (const char *s) {_data = new char[_len+1];     memcpy (_data, S, _len);   _data[_len] = ' + ';     } public:mystring () {_data = NULL;   _len = 0;     } MyString (const char* p) {_len = strlen (p);   _init_data (P);     } MyString (const mystring& str) {_len = Str._len;     _init_data (Str._data); Std::cout << "Copy Constructor is called!   Source: "<< str._data << Std::endl;       } mystring& operator= (const mystring& str) {if (This! = &str) {_len = Str._len;     _init_data (Str._data); } std::cout << "Copy assignment is called!     Source: "<< str._data << Std::endl;   return *this;   } virtual ~mystring () {if (_data) free (_data);  }  };   int main () {MyString A;   A = MyString ("Hello");   Std::vector<mystring> VEC;  Vec.push_back (MyString ("World")); }

Operation Result:

Copy Assignment is called! Source:hello  Copy Constructor is called! source:world

This string class has basically met the needs of our presentation. In the main function, the operation of the call copy constructor and the copy assignment operator are implemented. MyString ("Hello") and MyString ("World") are temporary objects, which are right values. Although they are temporary, the program still invokes copy construction and copy assignment, resulting in meaningless resource requests and actions released. If you can directly use the resources that the temporary object has already requested, you can save resources and save time on resource requests and releases. This is precisely the purpose of defining the transfer semantics.

Let's define the transfer constructor first.

  MyString (mystring&& str) {     std::cout << Move Constructor is called! Source: "<< str._data << Std::endl;     _len = Str._len;     _data = Str._data;     Str._len = 0;     Str._data = NULL;  }

Similar to copy constructors, there are a few things to note:

1. The symbol for the parameter (right value) must be an rvalue reference symbol, or "&&".

2. The parameter (right value) cannot be a constant, because we need to modify the rvalue.

3. Resource links and tags for parameters (rvalue) must be modified. Otherwise, the destructor for the right value frees the resource. The resources transferred to the new object are also invalid.

Now we define the transfer assignment operator.

  mystring& operator= (mystring&& str) {     std::cout << "Move assignment" is called! Source: "<< str._data << Std::endl;     if (this = &str) {       _len = Str._len;       _data = Str._data;       Str._len = 0;       Str._data = NULL;     }     return *this;  }

The problem to note here is the same as the transfer constructor.

With the addition of the transfer constructor and the transfer copy operator, our program runs with the following results:

Move Assignment is called! Source:hello  Move Constructor is called! source:world

As a result, the compiler distinguishes the left and right values, calls the transfer constructor and the transfer assignment operator on the right value. It saves resources and improves the efficiency of program operation.

With Rvalue reference and transfer semantics, when we design and implement classes, we design transfer constructors and transfer assignment functions to improve the efficiency of the application for classes that require a large amount of resources to be dynamically requested.

Standard library function Std::move

Since the compiler only calls the transfer constructor and the transfer assignment function on rvalue references, all named objects can only be lvalue references, and if it is known that a named object is no longer in use and wants to call the transfer constructor and the transfer assignment function on it, that is, to use an lvalue reference as an rvalue reference, how do you do it? The standard library provides the function std::move, which converts an lvalue reference to an rvalue reference in a very simple way.

Sample program:

void Processvalue (int& i) {   std::cout << "LValue processed:" << i << Std::endl;  }  void Processvalue (int&& i) {   std::cout << "RValue processed:" << i << Std::endl;  }  int main () {   int a = 0;   Processvalue (a);   Processvalue (Std::move (a));  

Operation Result:

LValue processed:0  RValue processed:0

std::moveIt is very helpful to improve the performance of the Swap function, in general, the swap general definition of the function is as follows:

    Template <class t> Swap (t& A, t& b)     {         T tmp (a);   Copy A to tmp         a = b;      Copy B to a         B = tmp;    Copy tmp to B  }

With the Std::move,swap function, the definition becomes:

    Template <class t> Swap (t& A, t& b)     {         T tmp (Std::move (a));//move A to tmp         a = Std::move (b); 
   //move B to a         B = std::move (TMP);  Move tmp to B  }

With Std::move, a simple swap function avoids 3 unnecessary copy operations.

Precise delivery (Perfect Forwarding)

This paper uses precise transmission to express this meaning. "Perfect Forwarding" is also translated into the perfect forwarding, accurate forwarding, etc., said is a meaning.

Precise delivery applies to scenarios where a set of parameters needs to be passed intact to another function.

"Intact" is not just the value of the parameter, but in C + + There are two sets of properties in addition to the parameter values:

Left/Right values and const/non-const. Accurate delivery means that all of these properties and parameter values cannot be changed during parameter passing. In generic functions, such requirements are very common.

The following examples illustrate. The function forward_value is a generic function that passes one argument to another function Process_value.

The forward_value is defined as:

Template <typename t> void Forward_value (const t& val) {   process_value (val);  }  Template <typename t> void Forward_value (t& val) {   process_value (val);  }

The function Forward_value must overload two types of,t& and const T& for each parameter, otherwise, the following four different types of parameters cannot be satisfied in the call:

  int a = 0;   const int &b = 1;   Forward_value (a); int&   Forward_value (b);//const int&  forward_value (2);//int&

For a parameter to be overloaded two times, that is, the number of function overloads and the number of parameters is a proportional relationship. The number of definitions for this function is very inefficient for programmers. Let's look at how rvalue references can help us solve this problem:

Template <typename t> void Forward_value (t&& val) {   process_value (val);  }

You just need to define it once and accept an rvalue reference parameter to pass all the parameter types intact to the target function. Four calls without type parameters are satisfied, and the left and right values of parameters and const/non-cosnt properties are fully passed to the target function Process_value. Isn't this solution simple and elegant?

  int a = 0;   const int &b = 1;   Forward_value (a); int&   Forward_value (b);//const int&   forward_value (2);//int&&

The derivation rules for the t&& defined in C++11 are:

An rvalue argument is an rvalue reference, and an lvalue argument is still an lvalue reference.

In a word, the attribute of the parameter is not changed. This also perfectly achieves the complete transfer of the parameters.

Rvalue reference, on the surface, only adds a reference symbol, but it has a very big impact on C + + software design and class library design. It can simplify the code, but also improve the efficiency of program operation. Every C + + software designer and programmer should understand and be able to apply it. When we design the class, we should design the transfer constructor and transfer copy function if there is a dynamic request for resources. When designing a class library, you should also consider std::move usage scenarios and actively use it.

Summarize

Rvalue reference and transfer semantics are an important feature of the new C + + standard. Every professional C + + developer should be mastered and applied to the actual project. When you have the opportunity to refactor your code, you should also think about whether you can apply a new line. Before you use it, you need to check the compiler's support.

C++11 rvalue Reference and transfer semantics

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.