24th. Right-value reference of the C++11 attribute

Source: Internet
Author: User

Rvalue reference is one of the most important improvements in the C++11 language core. Rvalue references bring "Move Semantics" ("Transfer Semantics") to C + + and solve the problem of perfect forwarding in template programming (Perfect forwarding). rvalue references enable C + + objects to identify what is (and can be considered) a temporary object, and to do some special processing of a copy of a temporary object, typically by simply passing the ownership of the resource rather than copying it in a generic way, which is called move semantics. Perfect forwarding means that when the template is programmed, the parameters ' attributes ' (Lvalue/rvalue, const, and so on) are not lost when each level function parameter is passed. Both of these issues are good for improving the efficiency of C + + programs, allowing C + + programs to more precisely control the behavior of resources and objects (I think, at the same time, to a certain extent, also improve the form of C + + program "Freedom").

As long as the English is basically passable, it is recommended to read the following pages of the article, they are world-class experts, the understanding of c++11 more accurate than I:

1. For example, clearly stating "why", but not "how", before Google Row, Scott Meyers also recommended (below)

c++ Rvalue References explained 
http://thbecker.net/articles/rvalue_references/section_01.html

universal References in C++11-scott meyers < br> http://isocpp.org/blog/2012/11/ Universal-references-in-c11-scott-meyers

3. VC Library Function Development Group of the teacher wrote, this I think the technical details are very clear. But very long. Make the "how" very clear.

rvalue references:c++0x Features in VC10, part 2 
/http:/ Blogs.msdn.com/b/vcblog/archive/2009/02/03/rvalue-references-c-0x-features-in-vc10-part-2.aspx

If you want to see the Simplified Chinese version, continue down, this is basically the above information 3 notes

First, how to distinguish the left and right values

To understand rvalue references (and the "references" we've been using, called Lvalue references later), it's time to figure out what "lvalue, Rvalue" is. First, the Lvalue rvalue is for the expression, and if an expression represents something that can be accessed after the expression statement is finished (that is, there is a real name for it), then the expression is an lvalue, otherwise it is the right value. My understanding is that "the value of an expression that requires the compiler to generate temporary" anonymous "variables for the program is the right value. But human is not a compiler, it is not convenient to use this method to determine the left and right values. The simplest way to determine whether an expression is an lvalue or an rvalue is to "address this expression." Here are some examples:

    1. int x = 0;
    2. x + 1; Rvalue
    3. X Lvalue
    4. ++x; Lvalue
    5. x + +; Rvalue
    6. int y[10];
    7. Y[0]; Lvalue
    8. "Literal string"//Rvalue

The reason why "address this expression" can be used as an expression is an lvalue or Rvalue method, because the C + + standard says that the address can only be applied to the left value. ((c++03 5.3.1/2).) (with exception, reference 3 says VC can open an extended option for C + +, but a general upright programmer won't use it!) :))。 Because the rvalue is treated as an "anonymous" variable (or a variable added by the compiler), it is a ghost variable in the program, the programmer does not need to know its existence, and should not deal with it, so if you can take an address to the right value, there are many behaviors that are dangerous to the program.

A more common example:

    1. x = a + B + C + D;

In fact, the compiler generates something like the following pseudo-code.

    1. Temp1 = a + b;
    2. Temp2 = Temp1 + C;
    3. x = Temp2 + D;

The temp in this example is the right value (the name of temp is only known by the compiler and is not known by the program).

(The reason I guess is two, one is because most of the machine instructions supported by the Cup are two operators (most of the operators are also two operands), the other is to simplify the complex formula into a simple formula (binary tree form syntax tree) and eventually translated into a compilation is the compiler's mature algorithm, Lexical analysis is basically doing the job. (pure speculation, non-study, not studied))

The main reason we pay attention to the right value is that the right value sometimes leads to unnecessary performance overhead. To cite a similar example, if x, Y, Z are objects of a class (a), a dynamically requests a resource (such as a large memory) when constructed, and in a copy-constructed function, the same size is applied first, and then the contents of the copied meta-object are copied to the memory you just requested. So, when we write down z = x + y, the overloaded operator function + The TEMP variable generated by temp, calculates the value of x + y in temp, (this temp is unavoidable, because + should not change the value of the operand), then Z calls the copy constructor, requests a chunk of memory, The value of temp is copied over, and finally, temp calls the destructor and frees up its own memory. In fact, if you are preoccupied, you will find that temp is just a temporary transition, anyway z = x + y this statement after the end of the temp is meaningless (also be analyzed), then why not the temp memory to Z, so that Z does not need to request the memory, do not need to copy its value, temp No need to free up memory, save a lot of expenses ah.

Yes, of course we want to do this, but before C++11, the program has no way to tell if the copy object is not a temporary variable (variable rvalue), is it safe to steal the resources from this object, we can only write a "copy constructor", yes, we have been writing the kind of "copy with" 's constructor. Now you see why the addition of rvalue references in C + + means that the program can tell if an object is a "ghost object" and then "treats" the object that passed in the function differently. But don't worry, just look at the type of reference.

Ii. Types of references

Now, the reference types in c++11 are divided into the following categories:

    • Variable lvalue reference: Type &
    • Constant lvalue Reference: Type const &
    • Variable rvalue reference: Type &&
    • Constant Rvalue reference: type const &&

How can these references be bound to "values" at initialization time? Follow two rules:

    1. To follow the "constant correct" principle, that is, a very literal reference (type & and type &&) cannot be bound to a constant (constant lvalue, constant right value)
    2. Prevents accidental modification of "ghost variables", so mutable lvalue references (type &) cannot be bound to the right value

Summing up is the diagram of the following binding:

In c++03, we already know type & and type const & these two types of parameters can participate in function overloading, now C++11 joins two new reference types, but also can participate in overloading, the rules of overloading are as follows:

    1. Cannot violate initialization binding rules
    2. Lvalue tends to select (constant) lvalue references, and rvalue tends to select (normal) rvalue references (strong)
    3. The very magnitude tends to select a very small reference (weak)

There are examples that will be easier to understand:

Reference overloaded Example 1
  1. #include <iostream>
  2. #include <string>
  3. #include <iomanip>
  4. using namespace Std;
  5. void Reference_overload (String & str) {
  6. COUT<<SETW (<<str<< "=" << "Type &" <<endl;
  7. }
  8. void Reference_overload (String && str) {
  9. COUT<<SETW (<<str<< "=" << "type &&" <<endl;
  10. }
  11. void Reference_overload (String const & STR) {
  12. COUT<<SETW (<<str<< "=" << "type const &" <<endl;
  13. }
  14. void Reference_overload (String const && str) {
  15. COUT<<SETW (<<str<< "=" << "type cosnt &&" <<endl;
  16. }
  17. String Const getconststring () {
  18. return string ("Const_rvalue");
  19. }
  20. int main () {
  21. String Lvalue ("Lvalue");
  22. String Const Const_lvalue ("Const_lvalue");
  23. Reference_overload (Lvalue);
  24. Reference_overload (Const_lvalue);
  25. Reference_overload (String ("rvalues"));
  26. Reference_overload (Getconststring ());
  27. }

The results of the operation are as follows:

In line with our expected imagination. In practice, however, there is no need to overload these four types, but only to:

    • Type Const &
    • Type &&

It is useful to overload two reference types, and what happens when there are only two types of overloads?

Reference overloaded Example 2
  1. #include <iostream>
  2. #include <string>
  3. #include <iomanip>
  4. using namespace Std;
  5. void Reference_overload (String && str) {
  6. COUT<<SETW (<<str<< "=" << "type &&" <<endl;
  7. }
  8. void Reference_overload (String const & STR) {
  9. COUT<<SETW (<<str<< "=" << "type const &" <<endl;
  10. }
  11. String Const getconststring () {
  12. return string ("Const_rvalue");
  13. }
  14. int main () {
  15. String Lvalue ("Lvalue");
  16. String Const Const_lvalue ("Const_lvalue");
  17. Reference_overload (Lvalue);
  18. Reference_overload (Const_lvalue);
  19. Reference_overload (String ("rvalues"));
  20. Reference_overload (Getconststring ());
  21. }

The results of the operation are as follows:

As seen from the above results, C++11 's reference overload rules implement overloads of type Const & and type &&, and the program can "differentiate" what the value of a variable can be "stolen from unnoticed" and what the value of the variable is not dynamic. With this approach, the designer of the class can design a copy constructor for the "Move Semantics".

Third, Move!

In fact, there are more relaxed, there is not much to remember things, for a simple example

    1. A (const a& _right) {
    2. This->p = new INT[100];
    3. memcpy (This->p, _RIGHT.P, 100);
    4. }
    5. A (a && _right) {
    6. This->p = _RIGHT.P;
    7. _RIGHT.P = nullptr;
    8. }

Rows 1 through 4 are copy constructors, and 5 to 8 rows are transfer constructors (move). In the copy constructor, we construct a new object from a (constant) lvalue, or a constant right value as a copy source, and the original object cannot be modified, so it is only possible to re-update a new piece of the area and copy the data. However, if the copy source object is a very good value, it means that this is an "unattended" variable, we can get the resources owned by the variable by the pointer exchange, and the "source object" is pointed to null, anyway, this structure is the end of this, the no-care variable is destructor.

From the above example it is easy to see that the use of this escape structure in the right place can greatly improve the performance of the program.

Of course these are not enough, there are many places, we also want to use the transfer structure, but there may be problems, such as the following example:

    1. A Geta () {
    2. A temp;
    3. Do sth.
    4. return temp;
    5. }
    6. A x = Geta ()

As a programmer, I know that the TEMP variable is useless after the return temp, so I'd like to be able to transfer the contents of the TEMP variable to the variable x with the transfer copy constructor, but temp is an lvalue! Transfer copy constructor is not selected! Is there no way out? It would be nice if we could "convert" the Lvalue to an rvalue and let the compiler call the transfer constructor. This is a very "general requirement", except for the example above, where we will encounter a number of similar situations, such as when I want to use the "assignment operator" to implement a copy constructor:

    1. A (const a& _right) {
    2.     this->p = new int[ ;
    3.     memcpy (This->p, _RIGHT.P,);
    4. }
    5. A (a && _right) {
    6.     this->p = nullptr
    7.  & Nbsp;  *this = _right; //Unexpected thing happens
    8. }
    9. A& operator  = (A && _right) {
    10.     this->p = _RIGHT.P;
    11. &NBSP;&NBSP;&NBSP;&NBSP;_RIGHT.P = nullptr;
    12.  
    13.     return *this;
    14. }
    15. a& operator = (a const & _right) {
    16.      This->p = new int[100];
    17.     memcpy (This->p, _RIGHT.P,);
    18.  
    19.     return *this;
    20. }

Line 7th, we expect it to call the "Transfer assignment operator" on line 9th, but actually call the "normal assignment operator" on line 15th,

Because in C + +

    1. The left value of the named is the left value
    2. An unnamed Lvalue reference is a left-hand value
    3. An rvalue reference to a named value is an lvalue
    4. An unnamed rvalue reference is a right value

So the above example, ultimately, does not achieve the effect we want.

What do we do? C++11 provides a way to do this: Std::move, now the program will be:

    1. A Geta () {
    2. A temp;
    3. Do sth.
    4. return Std::move (temp);
    5. }
    6. A x = Geta ()

Okay, now the transfer constructor is called, like magic, right? Std::move This name is not good, in fact, it does not have "move" anything, it just to "not" the very right value "into a very good value". But how did that happen? It's not that hard:

    1. Template <typename t> struct Removereference {
    2. typedef T type;
    3. };
    4. Template <typename t> struct removereference<t&> {
    5. typedef T type;
    6. };
    7. Template <typename t> struct removereference<t&&> {
    8. typedef T type;
    9. };
    10. Template <typename t> typename removereference<t>::type&& Move (t&& T) {
    11. return t;
    12. }

With this move, there is a way to convert Lvalue, rvalue, lvalue references, rvalue references to rvalue references.

Well, so far, it should be clear that the Lvalue rvalue, and their references, as well as the transfer semantics, are basically the same.

Move semantics and rvalue reference, do make C + + more "complex", pay the cost of course there will be a return, STL performance has been greatly improved, think of vector<string> V; Such an object, whenever the memory area of V needs to be enlarged or shrunk, when there is no move, each string stored in V needs to be re-copied once, but not now, they only need to exchange pointers. And all of this you just need to change a compiler to get, completely do not need to modify the previous program. Of course, in the future when we write a class that dynamically applies for resources (requiring a deep copy of the class (implementing the Rule Three)), if we implement the function of transferring copy construction and assignment on this class, we can also gain this performance benefit when using STL and some other functions. Moreover, it makes the "pass value" less scary in C + +. You can no longer have to compromise on the form of a program for "performance", such as using reference parameters instead of return values to get the output of a function, and so on.

As a "no how to write templates and libraries," The programmer, I think I understand the content so far is enough. And these things are not easy to digest.

Perfect forwarding, will be left to the next time to learn the strength of the ~

24th. Right-value reference of the C++11 attribute

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.