Analysis on rvalue reference, transfer semantics and perfect forwarding in c++11

Source: Internet
Author: User
1. The left and right values:

C + + has no standard definition for lvalue and rvalue, but there is a widely accepted saying that the address can be taken, the name, the non-temporary is the left value, no address, no name, temporary is the right value.

The immediate number is visible, the value returned by the function is the right value, and not the anonymous object (including the variable), the reference returned by the function, the const object, and so on are all lvalue values.

In essence, creation and destruction are controlled by the compiler, and the programmer can only ensure that the right value (including the immediate number) is valid in our code, and that the user creates, through the scope rules, the lifetime of the Lvalue (including the reference to the local variable returned by the function and the const object), for example:

int& foo () {int tmp; return tmp;} int fooo () {int tmp; return tmp;} int a=10; const int B; int& Temp=foo ();//Although valid, temp references an object that does not already exist int tempp=fooo ();

In the above code, A,TEMP and foo () are very important lvalue, B is a constant lvalue, fooo () is a very good right value, 10 is a constant right value, one thing to pay special attention: The return of the reference is an lvalue (can take the address)!

In general, the compiler does not allow changes to the rvalue (because the lifetime of the rvalue is not mastered by the programmer, even if the right value is changed may not be available), especially for built-in type objects, but C + + allows the use of Rvalue objects to invoke member functions, although this is allowed, but for the same reason, it is best not to do so

2. Rvalue reference:

An rvalue reference is represented by

datatype&& variable

Rvalue references are new to C + + 11, so a reference to C + + 98 is an lvalue reference. An rvalue reference is used to bind to an rvalue, and the lifetime of an rvalue bound to an rvalue that would otherwise be destroyed extends to the lifetime of the Rvalue reference bound to it, and the existence of an rvalue reference is not intended to replace an lvalue reference (in particular, temporary objects) are constructed to reduce object construction and destruction operations to achieve efficiency gains, for example for the following functions:

(Demo is a class) Demo foo () {  demo tmp;  return TMP;}

The compiler does not perform RVO (return value optimization) optimizations under the premise of the following actions:

Demo X=foo ();

A three-time constructor (TMP, X, temporary object) will be called, and three destructors will be called when the object is destroyed, if the Rvalue reference is used:

demo&& X=foo ();

Then there is no need to construct X, and the temporary object that would have been destroyed would have extended the lifetime to x as well because of the binding of x (it could be understood that X gave the temporary object a legal status: a name), and it needed to be more efficient (at the cost of the TMP taking up 4 bytes of space, but it was trivial) .

Rvalue Reference and Lvalue reference binding rules:

A constant lvalue reference can be bound to a constant and a very good lvalue, a constant and a very good right value;

A very value Lvalue reference can only be bound to a very left value;

A very good rvalue reference can only be bound to a very right value (vs2013 can also be bound to a constant right value);

A constant rvalue reference can only be bound to a constant and a very good rvalue (a very good rvalue reference exists only for the completeness of the semantics, and a constant lvalue reference will do the work).

Although it can be seen from the binding rule that a constant lvalue reference can also be bound to an rvalue, it is not possible to change the value of the rvalue, and the rvalue reference is possible to implement the transfer semantics, because the rvalue reference is usually changed to the right value of the binding, so the right value of the binding cannot be const.

Note: Rvalue references are left values!

3. Transfer semantics (move semantics):

One of the purposes for which rvalue references are introduced is to implement transfer semantics, which can transfer ownership of resources (heaps, system objects, and so on) from one object (usually an anonymous temporary object) to another, reducing object build and destroy operations and improving program efficiency (as explained in the 2 example). Transfer semantics are relative to copy semantics. As you can see from the transfer semantics, the transfer semantics are not new concepts, they are actually used in c++98/03 languages and libraries, such as the omission of copy constructors in some cases (copy constructor elision in Some contexts), a copy of the smart pointer (auto_ptr "copy"), a link list (list::splice) and a permutation within the container (swap on containers), but no uniform syntax and semantic support

Although normal functions and operators can also use rvalue references to implement transfer semantics (as in the example in 2), the transfer semantics are usually implemented by transferring constructors and transfer assignment operators. The prototype of the transfer constructor is classname (typename&&), While the copy constructor is classname (const typename&), the transfer constructor is not automatically generated by the compiler, it needs to be defined by itself, only the transfer constructor is defined, and the compiler generates a copy constructor, and if the passed argument is an lvalue, the copy constructor is called , instead, the transfer constructor is called.

For example:

Class demo{Public:   Demo ():p (New int[10000]{};   Demo (demo&& lre): Arr (Lre.arr), size (lra.size) {lre.arr=null;} Transfer constructor   Demo (const demo& LRE): arr (new int[10000]), size (arr.size) {for     (int cou=0;cou<10000;++cou)       Arr[cou]=lew.arr[cou];   } Private:   int size;   int* arr; }

As can be seen from the above code, the copy constructor in the heap re-opened a size of 10000 int array, and then each element is copied, and the transfer constructor is directly take over the parameters of the pointer to the resource, the efficiency of the contract! It is important to note that the transfer constructor argument must be an rvalue, usually a temporary object, such as the return value of the function, for such temporary objects are usually destroyed after the line code, and the use of the transfer constructor can prolong its lifetime, is the most There is also the avoidance of re-opening the array. For the transfer constructor in the above code, it is necessary to analyze it in detail:

Demo (demo&& lre): Arr (Lre.arr), Size (lre.size) ({lre.arr=null;}

LRE is an rvalue reference that, through its indirect access to the resources of an argument (temporary object), completes the resource transfer, lre the bound object (must) is an rvalue, but Lre itself is an lvalue;

Because Lre is a local object of a function, "lre.arr=null" is necessary, otherwise the end of the function calls the destructor to destroy the LRE when the resource is still freed, the transferred resource or the system is retracted.

4. Move () function

The example in 3 is not omnipotent, the actual argument of the Demo (demo&& lre) must be an rvalue, sometimes an lvalue is about to reach the lifetime, but still want to use the transfer semantics to take over its resources, you need the move function.

The Std::move function is defined in the standard library <utility>, and its role is to forcibly convert the left value to the use of the right value, from the implementation, Std:move is equivalent to static_cast<t&&> (Lvalue), As a result, the lifetime and Lvalue properties of the transformed Lvalue itself are not changed, which is similar to the const_cast function. Therefore, the actual argument of the move should be the left-hand value of the impending lifetime, otherwise it may have a negative effect.

5. Perfect Forwarding (perfect forwarding)

Perfect forwarding refers to the "perfect" transfer of a set of arguments to a formal parameter, which is perfectly defined as the const attribute of the parameter and the left-right value property, such as the following overloads of the Func function when the function wrapper is carried out:

void func (const int); void func (int); void func (int&&);

If you want to wrap them inside a function cover, implement:

void cover (typename para) {  func (para);}

Makes it possible to invoke a function of the corresponding type in cover for different arguments, which seems to be possible only by overloading the cover, which makes the code cumbersome, and the other is to use a function template, but before C + + 11, the function template that implements the function can only be passed by value, as follows:

Template<typename t>void cover (T para) {  ...  Func (para);  ...}

However, if you pass a fairly large object and create an efficiency problem, to pass the perfect match of the formal participation argument by reference (the perfect match between the parcel const attribute and the left-right value attribute), you will use the new reference collapse rule introduced by C + + 11:

function parameters after the derivation of the type of the function parameter T

t& a& a&
t& a&& a&
t&& a& a&
t&& a&& a&&

Therefore, for the previous function packaging requirements, the following template can be used to solve:

Template<typename t>void cover (t&& para) {  ...  Func (static_cast<t &&> (para));  ...}

If an lvalue reference is passed in, the forwarding function is instantiated as:

void func (t& && para) {   func (static_cast<t& &&> (para));}

To apply a reference collapse, it is:

void func (t& para) {   func (static_cast<t&> (para));}

If an rvalue reference is passed in, the forwarding function is instantiated as:

void func (t&& &¶) {    func (static_cast<t&& &&> (para));}

Applying a reference collapse is:

void func (t&& para) {   func (static_cast<t&&> (para));}

For the above static_cast<t&&>, it only works when para is deduced as an rvalue reference, because Para is an lvalue (rvalue reference is an lvalue), so it needs to be converted to rvalue and then passed into Func, and C + + 11 <untility> defines a std::forward<t> function to achieve the above behavior,

So the final version

Template<typename t> void cover (t&& para) {   func (Forward (forward<t>));}

Std::forward implementation is slightly different from static_cast<t&&> (para)

The use of the Std::forward function is forward<t> (para), and if T is an lvalue reference, para will be converted to an lvalue of type T, otherwise para will be converted to the T-type right value

Summarize

The above is about the c++11 in the right value reference, transfer semantics and perfect forwarding of all the content, this article is introduced in very detailed, I hope that everyone's study work can be helpful.

Related Article

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.