C + + 11 Rvalue reference

Source: Internet
Author: User

A very important concept introduced in c++11 is the rvalue reference. Understanding Rvalue references is the basis for learning "move Semantics". To understand rvalue references, you must first differentiate between lvalue and rvalue values.

Note: Lvalue rvalue translation may have some problems *l-value L refers to location, which means addressable. R in *r-value refers to read, which indicates readable.

One of the most common misconceptions about Lvalue and Rvalue is that the left side of the assignment operator is the Lvalue, and the right side of the assignment operator is the rvalue. Both Lvalue and rvalue are for expressions, and an lvalue is a persisted object that persists after an expression ends, and an rvalue is a temporary object that no longer exists at the end of an expression. A convenient way to differentiate between Lvalue and rvalue is to see if you can take an address on an expression, or, if so, an lvalue, or a right value. Here are some examples to illustrate:

1 intA =Ten;2 intb = -;3 int*ptr = &A;4vector<int>Vec;5Vec.push_back (1);6 stringSTR1 ="Hello";7 stringSTR2 =" World";8 Const int& M = A;

Excuse me, A, B, A+b, a++, ++a, PTR, *ptr, vec[0], 100, String ("Hello"), str1, STR1+STR2, M respectively is the left or right value?

Both A and B are persistent objects (which can be addressed) and are left values;

A+b is a temporary object (it can not take address), is the right value;

A++ is to take out a copy of persistent object A, and then the value of persistent object a plus 1, and finally return the copy, and that copy is a temporary object (can not address it), it is the right value;

++a is the value of persistent object a plus 1, and return the persistent object a itself (can be taken to address), it is an lvalue;

Both PTR and *ptr are persistent objects (which can be addressed) and are left-valued;

VEC[0] invokes the overloaded [] operator, whereas the [] operator returns a int&, which is a persistent object (which can be addressed to), is an lvalue;

100 and string ("Hello") is a temporary object (it can not be taken to address), is the right value;

STR1 is a persistent object (which can be addressed) and is an lvalue;

STR1 + str2 is called the + operator, and the + operator returns a string (no address can be taken), so it is the right value;

M is a constant reference, referring to an rvalue, but the reference itself is a persistent object (which can be addressed) and is an lvalue.

The left and right values are clearly distinguished, and then the Lvalue reference is seen. An lvalue reference can be divided into a very good lvalue reference and a constant lvalue reference, depending on its modifier.

A very good lvalue reference can only bind a very left value, not a constant lvalue, a very right value, or a constant right value. If you allow binding to constant lvalue and constant rvalue, a non-const lvalue reference can be used to modify constant lvalue and constant rvalue, which clearly violates the meaning of the constant. If you allow binding to a very bad right value, it can cause very dangerous situations, because the very right value is a temporary object, and a very good lvalue reference might use a temporary object that has been destroyed.

A constant lvalue reference can be bound to a value of all types, including a non-const lvalue, a constant lvalue, a very good right value, and a constant right value.

As you can see, when using an lvalue reference, it is not possible to distinguish whether a binding is a very right value. So why do we differentiate between the very right values and what are the benefits of distinguishing them? This involves a well-known performance issue in C + +-copying temporary objects. Consider the following code:

 1  vector<int  > Getallscores ()  2  { 3  vector<int  > vcttemp;  4  vcttemp.push_back (90  ); 5  vcttemp.push_back (95  ); 6  return     vcttemp;  7 } 

When initialized with vector<int> Vctscore = Getallscores (), a three-time constructor is actually called. Although some compilers can use Rvo (Return Value optimization) to initialize, optimizations work only under certain conditions. As you can see, a very common function call above, due to the existence of a copy of the temporary object, resulting in an additional two copies of the constructor and the cost of the destructor. Of course, we can also modify the function in the form of void Getallscores (vector<int> &vctscore), but this is not necessarily the form we need. Also, consider the link operation for the following string:

 1  string  S1 (  " hello   "  2  string  s = s1 +  " a  "  +  b  "  +  c  "  +  d  "  +  e  " ; 

When the S is initialized, it produces a large number of temporary variables and involves copying operations of a large number of strings, which can obviously affect the efficiency and performance of the program. How to solve this problem? If you can determine that a value is a very good value (or a left value that will not be used later), you can copy the temporary object without copying the actual data, but simply "stealing" a pointer to the actual data (similar to the auto_ptr in the STL, which transfers ownership). An rvalue reference introduced in c++11 can be used to identify a very good-value right. Use & in c++11 to denote lvalue references, with && for rvalue references, such as:

int ten;

Rvalue references can also be divided into very good rvalue references and constant rvalue references, depending on their modifiers.

A very good rvalue reference can only bind a very right value, and cannot be bound to a very good lvalue, a constant lvalue, and a constant right value. If you allow binding to a non-const lvalue, you may mistakenly steal data from a persisted object, which is very dangerous, and if you allow binding to constant lvalue and constant rvalue, a non-const rvalue reference can be used to modify constant lvalue and constant rvalue, which is a clear violation of the meaning of its constant.

A constant rvalue reference can be bound to a very right value and a constant right value, and cannot be bound to a very good lvalue and a constant lvalue (for the same reason).

With the concept of rvalue referencing, we can use it to implement the following CMyString class.

1 classcmystring2 {3  Public:4CMyString (Const Char* str =nullptr)5     //constructor Function6     {7cout <<"cmystring (const char *STR = nullptr)"<<Endl;8         if(str = =nullptr)9         {TenM_pdata =New Char[1]; One*m_pdata =' /'; A         } -         Else -         { theM_pdata =New Char[Strlen (str) +1]; - strcpy (m_pdata, str); -         } -     } +      -CMyString (Constcmystring&s) +     //copy Constructor A     { atcout <<"cmystring (const cmystring &s)"<<Endl; -M_pdata =New Char[Strlen (S.m_pdata) +1]; - strcpy (M_pdata, s.m_pdata); -     } -      -CMyString (cmystring &&s) in     //Move Constructor -     { tocout <<"cmystring (cmystring &&s)"<<Endl; +M_pdata =S.m_pdata; -S.m_pdata =nullptr; the     } *      $~cmystring ()Panax Notoginseng     // Destructors -     { thecout <<"~cmystring ()"<<Endl; +         Delete[] m_pdata; AM_pdata =nullptr; the     } +      -cmystring&operator=(Constcmystring&s) $     //Copy Assignment function $     { -cout <<"cmystring& operator= (const cmystring& s)"<<Endl; -         if( This! = &s) { the             Delete[] m_pdata; -M_pdata =New Char[Strlen (S.m_pdata) +1];Wuyi strcpy (M_pdata, s.m_pdata); the         } -         return* This; Wu     } -      Aboutcmystring&operator= (cmystring&&s) $     //Move Assignment Function -     { -cout <<"cmystring& operator= (cmystring&& s)"<<Endl; -         if( This! = &s) { A             Delete[] m_pdata; +M_pdata =S.m_pdata; theS.m_pdata =nullptr; -         } $         return* This; the     } the Private: the     Char*m_pdata; the};

As you can see, the move version of the constructor and assignment functions are added above. So, after adding the move version, what is the effect on the automatic generation rules of the class? The only effect is that if you provide a constructor for the move version, the default constructor is not generated. In addition, the compiler will never automatically generate a move version of the constructor and assignment functions, which need to be explicitly added.

When a move version of the constructor and the overloaded form of an assignment function is added, which overloaded version should a function call use? The following are the 3 rules listed according to the priority of the decision:

1, the constant value can only be bound to the constant reference, cannot bind to the very volume reference;

2, the Lvalue precedence is bound to the Lvalue reference, and the right value is first bound to the rvalue reference;

3, a very high value is first bound to a very high-volume reference.

The constructor or assignment function that invokes the move version can be derived from the decision rule given above when a constructor or assignment function is passed to a very right value. In the move version of the constructor or assignment function, it is directly "moved" the internal data of the pointer (because it is a very good value, is a temporary object, moving its internal data pointer does not cause any problems, it will be destroyed immediately, just re-use its memory), This eliminates the overhead of copying data.

One point to note is that the copy constructor can be implemented by calling *this = s directly, but the move constructor does not. This is because in the move constructor, S is a very rvalue reference, but it is essentially an lvalue (a persistent object that can be addressed), so when you call *this = S, the copy assignment function is used instead of the move assignment function, which does not match the semantics of the move constructor. To make the semantics correct, you need to bind the lvalue to a very rvalue reference, and C++11 provides the move () function to implement the conversion, so you can modify it to *this = Move (s) so that the move constructor calls the move assignment function.

C + + 11 Rvalue reference

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.