Lvalue references and rvalue references in C + + 11

Source: Internet
Author: User

1. First distinguish between left and right values
an lvalue is a persisted object that persists after an expression has ended
A right value is a temporary object that no longer exists at the end of an expression
Convenient method: Take an address to an expression, or an lvalue if it is possible, otherwise the right value
Example:
int a = ten
int b =
int *pflag = &a
vector<int> vcttemp
vcttemp.push_back (1)
string str1 = "Hello"
string str2 = "World"
const int &m = 1
Excuse me: a,b,a+b,a++,++a,pflag,vcttemp[0],100,string ("Hello"), str1+str2,m are left or right values respectively
A and B are persistent objects (which can be addressed) and are left-valued
A+b is a temporary object (no address can be taken), which is the right value
a++ is to remove a copy of the persistent object A, and then add 1 to the value of the persistent object A, and then return the copy, which is a temporary object (it can not be addressed to), so it is the right value
++a is the value of persistent object a plus 1, and returns the persistent object a itself (can be addressed to it), so it is the left value
Pflag and *pflag are persistent objects (which can be addressed) and are left-valued
Vcttemp[0] called the overloaded [] operator, and the [] operator returns an int &, which is a persistent object (which can be addressed), is an lvalue
100 and string ("Hello") is a temporary object (no address can be taken), which is the right value
str1 is a persistent object (which can be addressed), 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), an lvalue
To distinguish between Lvalue and rvalue, let's look at Lvalue references, and lvalue references can be classified as very lvalue references and constant lvalue references depending on their adornments.
A very good lvalue reference can only be bound to a very left value and cannot be bound to a constant lvalue, a very right value, and 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 its 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 may 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. Can be seen,
when using an lvalue reference, we cannot tell if the binding is a very good value. So why should we differentiate between the very right values and what are the benefits of distinguishing them? This involves a well-known performance issue in C + +---Copy temporary objects
vector<int> getallscores ()
 {
vector<int> vcttemp;
Vcttemp.push_back (+);
Vcttemp.push_back (+)
return vcttemp;
 }
When initialized with vector<int> Vctscore = Getallscores (), a three-time constructor is actually called. Although some compilers
The rvo (Return Value optimization) can be used for optimization, but the optimization works only under certain conditions. 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 connection operation of the following string
string S1 ("Hello");
string s = S1 + "A" + "B" + "C" + "D" + "E"
when the S is initialized, it produces a large number of temporary objects 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 we can determine that a value is a very good value (or an lvalue that will not be used later), then we can copy the temporary object without copying the actual data, but simply "stealing" pointers to the actual data (similar to the auto_ptr in the STL, transferring ownership) C + + The Rvalue reference referenced in 11 can be used to identify a very right value. Use & in c++11 to denote lvalue references, with && for rvalue references, such as:
int &&a = ten;
rvalue references can also be divided into very good rvalue references and constant rvalue references, depending on their modifiers.
A non-const rvalue reference can only be bound to a non-const right value, cannot be bound to a non-const lvalue, a constant lvalue, and a constant rvalue (VS2010 beta can be bound to a very good lvalue and a constant lvalue, but is not allowed in the official version for security reasons). 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.
class CMyString
{
Public :
//Constructors
cmystring (const char *PSZSRC = NULL)
    {
cout<< "cmystring (const char *PSZSRC = NULL)" <<endl;
if (pszsrc = = NULL)
        {
m_pdata = new char[1];
*m_pdata = ' + ';
        }
Else
        {
m_pdata = new Char [strlen (PSZSRC) + 1];
strcpy (M_PDATA,PSZSRC);
        }
    }
//Copy constructor
cmystring (const cmystring &s)
    {
cout<< "cmystring (const cmystring &s)" <<endl;
m_pdata = new Char[strlen (s.m_pdata) +1];
strcpy (m_pdata,s.m_pdata);
    }
//move constructor Function
cmystring (cmystring &&s)
    {
cout<< "cmstring (cmystring &&s)" <<endl;
m_pdata = s.m_pdata;
s.m_pdata = NULL;
    }
//destructor
~cmystring ()
    {
cout<< "~cmystring ()" <<endl;
Delete[]m_pdata;
m_pdata = NULL;
    }
//Copy assignment function
cmystring & operator = (const cmystring &s)
    {
cout<< "cmystring & operator = (const cmystring &s)" <<endl;
if (This! = &s)
        {
Delete[]m_pdata;
m_pdata = new Char [strlen (S.m_pdata) +1];
strcpy (m_pdata,s.m_pdata);
        }
return *this;
    }
//move Assignment Function
cmystring &operator = (cmystring &&s)
    {
cout<< "cmystring &operator = (cmystring &&s)" <<endl;
if (This! = &s)
        {
Delete[]m_pdata;
m_pdata = s.m_pdata;
s.m_pdata = NULL;
        }
return *this;
    }
Private:
Char *m_pdata;
};
as you can see, we added the constructor and assignment functions for the move version. So, what's the effect of adding a move version to a class's auto-generation rule? 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 constructors and assignment functions, which require you to explicitly add them manually.
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? Below is a press
The 3 rules are listed according to the priority of the decision:
1. A constant value can only be bound to a constant reference and cannot be bound to a very mass 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, we 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 non-constant rvalue reference, but itself 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 is inconsistent with the semantics of the move constructor. To make the semantics correct, we need to bind the lvalue to the very right value reference, and C + + 11 provides the move function to implement the conversion, so we can modify it to *this = Move (s) so that the move constructor calls the move assignment function.

Reference: Here

Lvalue references and rvalue references in C + + 11

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.