C + + standard (Ravalue Reference) right reference introduction _c language

Source: Internet
Author: User
Tags constant constructor shallow copy strlen volatile
1, the background of the introduction of the right value reference
The inefficiency caused by the generation and copying of temporary objects has been a problem that C + + has been accused of. But the C + + standard allows the compiler to have complete freedom over the generation of temporary objects, thereby developing compiler optimization techniques such as copyelision, RVO (including Nrvo), which prevent temporary object generation and copying in some cases. The following briefly describes Copyelision, RVO, which is not interested in the can skip directly:
(1) copyelision
Copyelision technology is designed to prevent the generation and copying of some unwanted temporary objects, such as:
Copy Code code as follows:

structa{
A (int) {}
A (consta&) {}
};
aa=42;

In theory, the above aa=42 statement will be divided into three steps: The first step is to construct a temporary object of type A by 42, and the second step is to construct a temporary object as a parameter copy and a third to deconstruct the temporary object. If a is a large class, then the construction and destructor of its temporary object will cause a large memory overhead. All we need is an object A, why not directly construct a with a parameter of 42? Copyelision technology is doing this optimization.
"description":You can add a print statement to the copy constructor of a, see if there is a call, and if not, congratulations, your compiler supports copyelision. But what needs to be said is: a copy of the constructor, although not called, but its implementation can not have access, do not believe you put it in the private permission to try, the compiler will certainly be an error.
(2) Return value optimization (rvo,returnvalueoptimization)
The return value optimization technique is also designed to prevent some unwanted temporary object generation and copying, for example:
Copy Code code as follows:

structa{
A (int) {}
A (consta&) {}
};
Aget () {Returna (1);}
Aa=get ();

Theoretically, the above aa=get () statement will be executed separately by creating a temporary object (assumed to be TMP1) in the Get () function first, then constructing the return value (assuming TMP2) as a copy of the parameter in TMP1, and finally constructing a with TMP2 as the parameter copy. It is accompanied by the deconstruction of TMP1 and TMP2. If a is a large class, then the construction and destructor of its temporary object will cause a large memory overhead. The return value optimization technique is designed to solve this problem by avoiding the generation and copying of two temporary objects Tmp1 and TMP2.
"description": a) You can add a print statement to the copy constructor of a, see if there is a call, and if not, congratulations, your compiler supports return value optimization. But what needs to be said is: a copy of the constructor, although not called, but its implementation can not have access, do not believe you put it in the private permission to try, the compiler will certainly be an error.
(b) In addition to the return value optimization, you may have heard of an optimization technique called named return value Optimization (NAMEDRETURNVALUEOPTIMIZATION,NRVO), which, from the programmer's point of view, actually has the same logic as Rvo. Only its temporary object has a variable name identifier, such as modifying the Get () function:
Copy Code code as follows:

Aget () {
Atmp (1);//#1
DoSomething
returntmp;
}
Aa=get ()//#2

Think about how many times a type A has an object construct after the above modification? Although the # # appears to be structured at once, #2处看起来也有一次显示地构造, but if your compiler supports Nrvo and copyelision, you will find the entire aa=get (), the execution of the statement, only one time the construction of a object. If you print the address of the TMP variable before the Get () function return statement, and then print a address after the Aa=get () statement, you will find that both addresses are the same, which is the result of applying the NRVO technique.
(3) Copyelision, Rvo unavoidable temporary object generation and copy
While technologies such as Copyelision and NVO (including Nrvo) avoid the generation and copying of temporary objects, they do not work in some cases, such as:
Copy Code code as follows:

Template<typenamet>
Voidswap (t&a,t&b) {
Ttmp (a);
A=b;
b=tmp;
}

We just want to exchange data for A and B two objects, but we have to use a temporary object tmp to back up one of the objects, and if the T-type object has data allocated to (or referenced) from the heap memory, the memory overhead of a deep copy is conceivable. To this end, the C++11 standard introduces a right value reference that enables the copy of a temporary object to have move semantics, so that the copy of the temporary object can be as efficient as a shallow copy, which in turn solves the efficiency impairment of the deep copy of the temporary object to some extent.

2, the C++03 standard in the left and right values
To understand the right value reference, first distinguish between the left value (lvalue) and the right value (rvalue).
The c++03 standard divides expressions into left and right values, and "not left or right":
Everyexpressioniseitheranlvalueoranrvalue.
The easiest way to distinguish whether an expression is left or right is to see if it can be addressed to it: if it is, it is the left value;
Description: Because of the introduction of the right value reference, the classification of the expression in the C++11 standard is no longer "not left or right", but for the sake of simple understanding, we only need to distinguish the left value right value for the time being, the classification in the C++11 standard will be described later.

3, the right value reference binding rules
Right-value references (rvaluereference,&&) are similar to traditional references (reference,&), in order to better differentiate between them, references in the traditional sense are referred to as left-value references (lvaluereference). The following is a simple summary of the binding rules for the left and right value references (with the exception of the function type Object):
(1) A non-const left value reference can only be bound to a non-const left value;
(2) A const left value reference can be bound to a const left value, not a const left value, a const right value, a non-const right value;
(3) A non-const right value reference can only be bound to a non-const right value;
(4) A const right value reference can be bound to a const right value and a non-const right value.
The test examples are as follows:
Copy Code code as follows:

Structa{a () {}};
alvalue;//non-const left value object
Constaconst_lvalue;//const left Value Object
Arvalue () {Returna ();} Returns a non-const right-value Object
Constaconst_rvalue () {Returna ();} Returns a const right value object
Rule one: Non-const left value reference can only be bound to a non-const left value
A&lvalue_reference1=lvalue;//ok
A&lvalue_reference2=const_lvalue;//error
A&lvalue_reference3=rvalue ();//error
A&lvalue_reference4=const_rvalue ();//error
Rule two: A const left value reference can be bound to a const left value, not a const left value, a const right value, a non-const right value
Consta&const_lvalue_reference1=lvalue;//ok
Consta&const_lvalue_reference2=const_lvalue;//ok
Consta&const_lvalue_reference3=rvalue ();//ok
Consta&const_lvalue_reference4=const_rvalue ();//ok
Rule three: A non-const right value reference can only be bound to a non-const right value
A&&rvalue_reference1=lvalue;//error
A&&rvalue_reference2=const_lvalue;//error
A&&rvalue_reference3=rvalue ();//ok
A&&rvalue_reference4=const_rvalue ();//error
Rule four: A const right value reference can be bound to a const right value and a non-const right value and cannot be bound to a left value
Consta&&const_rvalue_reference1=lvalue;//error
Consta&&const_rvalue_reference2=const_lvalue;//error
Consta&&const_rvalue_reference3=rvalue ();//ok
Consta&&const_rvalue_reference4=const_rvalue ();//ok
Rule five: Exception for function type
Voidfun () {}
Typedefdecltype (Fun) fun;//typedefvoidfun ();
Fun&lvalue_reference_to_fun=fun;//ok
Constfun&const_lvalue_reference_to_fun=fun;//ok
Fun&&rvalue_reference_to_fun=fun;//ok
Constfun&&const_rvalue_reference_to_fun=fun;//ok

"description":(1) Some support right reference but the lower version of the compiler may allow the right value reference to bind to the left value, such as g++4.4.4 allowed, but g++4.6.3 is not allowed, clang++3.2 also does not allow, it is said that the Vs2010beta version allowed, the official version is not allowed, I have no VS2010 environment, have not tested.
(2) The right value reference binding to a literal constant also conforms to the above rules, such as: int&&rr=123, where the literal value 123 is called a constant, but its type is int, not constint. The C++03 standard document 4.4.1 and its footnotes have the following description:
Iftisanon-classtype,thetypeofthervalueisthecv-unqualifiedversionoft.
Inc++classrvaluescanhavecv-qualifiedtypes (becausetheyareobjects). Thisdiffersfromisoc,inwhichnon-lvaluesneverhavecv-qualifiedtypes.
Therefore, the 123 non-const right value,int&&rr=123; statement conforms to the above rule three.

4, the c++11 standard in the expression classification
The introduction of right value references makes the classification of expressions in the C++11 standard no longer a non-left value or a right value, and the following figure is the classification of expressions in the C++11 standard:

The simple explanation is as follows:
(1) The lvalue is still the left value in the traditional sense;
(2) Xvalue (expiringvalue) literal meaning can be understood as the end of the life cycle of the value, It is the value of some expression that involves a reference to the right value (anxvalueistheresultofcertainkindsofexpressionsinvolvingrvaluereferences), For example, the return value of a function that returns a reference to the right value of a type is called Xvalue.
(3) Prvalue (purervalue) literal meaning can be understood as pure right value, also can be considered as the traditional sense of the right value, such as temporary objects and literal values.
(4) Glvalue (generalizedvalue) generalized left values, including traditional left values and xvalue.
(5) Rvalue in addition to the traditional meaning of the right value, but also includes Xvalue.
The above Lvalue and Prvalue, respectively, are consistent with the concepts of the left and right values in the traditional sense, and the xvalue is described as "some of the values of an expression involving a reference to a right value". The C++11 standard gives four distinct xvalue:
Copy Code code as follows:

[Note:anexpressionisanxvalueifitis:
--theresultofcallingafunction,whetherimplicitlyorexplicitly,whosereturntypeisanrvaluereferencetoobjecttype,
--acasttoanrvaluereferencetoobjecttype,
--aclassmemberaccessexpressiondesignatinganon-staticdatamemberofnon-referencetypeinwhichtheobjectexpressionisanxvalue , or
--a.*pointer-to-memberexpressioninwhichthefirstoperandisanxvalueandthesecondoperandisapointertodatamember.
Ingeneral, Theeffectofthisruleisthatnamedrvaluereferencesaretreatedaslvaluesandunnamedrvaluereferencestoobjectsaretreatedasxvalues ; Rvaluereferencestofunctionsaretreatedaslvalueswhethernamedornot.--endnote]
[Example:
structa{
Intm;
};
a&&operator+ (A,a);
A&&f ();
Aa;
A&&ar=static_cast<a&&> (A);
THEEXPRESSIONSF (), F () .m,static_cast<a&&> (A), anda+aarexvalues. Theexpressionarisanlvalue.
--endexample]

A simple understanding is that a named right value reference (Namedrvaluereference) is a left value, an unnamed right value reference (unamedrvaluereference) belongs to Xvalue, and a reference function type's right value reference whether or not a name is treated as a left value. An example is easier to understand:
[/code]
Arvalue () {Returna ();}
A&&rvalue_reference () {Returna ();}
Fun ()//Returns an unnamed right value reference, belonging to the Xvalue
A&&ra1=rvalue ();//ra1 is a named right value application, which belongs to the left value
A&AMP;&AMP;RA2=RA1;//ERROR,RA1 is treated as a left value, so RA2 cannot be bound to RA1 (not conforming to rule three)
A&la=ra1;//ok, non-const left value reference can be bound to a non-const left value (conforming to rule i)
Copy Code code as follows:

5. Move semantics
Now that we're back to 1-(3), which mentions move semantics, how do you make a copy of a temporary object have move semantics? Here we take the implementation of a class as an example:
[Code]
classa{
Public
A (constchar*pstr=0) {m_data= (pstr!=0?strcpy (Newchar[strlen (PSTR) +1],pstr): 0);}
Copyconstructor
A (consta&a) {m_data= (a.m_data!=0?strcpy (Newchar[strlen (A.m_data) +1],a.m_data): 0);}
Copyassigment
A&operator= (consta&a) {
if (this!=&a) {
Delete[]m_data;
M_data= (a.m_data!=0?strcpy (Newchar[strlen (A.m_data) +1],a.m_data): 0);
}
Return*this;
}
Moveconstructor
A (A&&a): M_data (a.m_data) {a.m_data=0;}
Moveassigment
A&operator= (a&&a) {
if (this!=&a) {
M_data=a.m_data;
a.m_data=0;
}
Return*this;
}
~a () {delete[]m_data;}
Private
Char*m_data;
};

As you can see from the example above, in addition to the traditional copy constructs (copyconstructor) and copy Assignment (copyassigment), we have added a mobile copy construct (Moveconstructor) and a move assignment (moveassigment) for the implementation of Class A. So, when we copy a (right) temporary object of Class A, a mobile copy constructor with move semantics is used to avoid calls to the strcpy () function in a deep copy, and when we assign a (right) temporary object to another object, Moves are assigned with move semantics, thus avoiding the invocation of the strcpy () function in the copy assignment. This is called move semantics.

6, the implementation of Std::move () function
With the move semantics in view, look at the efficiency problem in 1-(3):
Copy Code code as follows:

template<typenamet>//If T is ClassA
Voidswap (t&a,t&b) {
Ttmp (a);//According to the binding rules referenced by the right value, this does not call Moveconstructor, but calls Copyconstructor
a=b;//according to the binding rules referenced by the right value, Moveassigment is not invoked here, and the copyassigment is called
b=tmp;//according to the binding rules referenced by the right value, Moveassigment is not invoked here, and the copyassigment is called
}

As you can see from the example above, although we have implemented Moveconstructor and moveassigment, the example of the swap () function still uses the traditional copyconstructor and copyassigment. To get them to really use copy and copy of Move semantics, the Std::move () function comes in and looks at the following example:
Copy Code code as follows:

Voidswap (a&a,a&b) {
Atmp (Std::move (a));//std::move (a) is the right value, this will call Moveconstructor
A=std::move (b);//std::move (b) is the right value, and this will call Moveassigment
B=std::move (TMP);//std::move (TMP) is the right value, which is invoked here Moveassigment
}

We have to ask: binding rules three and four, which we apply to the right value, know that the right value reference cannot be bound to the left value, but how does the Std::move () function turn the above left values A, B, and TMP to the right value? This is to say from the implementation of the Std::move () function, in fact, the implementation of the Std::move () function is very simple, following the implementation in Libcxx library (in <type_trait> header file) as an example:
Copy Code code as follows:

Template<class_tp>
Inlinetypenameremove_reference<_tp>::type&&move (_tp&&__t) {
typedeftypenameremove_reference<_tp>::type_up;
Returnstatic_cast<_up&&> (__t);
}

The implementation of Remove_reference is as follows:
Copy Code code as follows:

template<class_tp>structremove_reference{typedef_tptype;};
template<class_tp>structremove_reference<_tp&>{typedef_tptype;};
template<class_tp>structremove_reference<_tp&&>{typedef_tptype;};

From the implementation of the move () function, you can see that the form parameter (Parameter) type of the move () function is a right value reference, how can it be bound to the left value A, B, and TMP as arguments (Argument)? This is not the binding rule that still does not conform to the right value application three! Simply put, if move is just a normal function (rather than a template function), then the binding rule three and rule four, based on the right value, do not use the left value as the actually parameter. But it is a template function, involving the derivation of template parameters, it is different. In the C++11 standard document 14.8.2.1, the derivation of the template function parameters is described as follows:
Templateargumentdeductionisdonebycomparingeachfunctiontemplateparametertype (CallitP) Withthetypeofthecorrespondingargumentofthecall (Callita) asdescribedbelow. (14.8.2.1.1)
Ifpisareferencetype, Thetypereferredtobypisusedfortypededuction.ifpisanrvaluereferencetoacvunqualifiedtemplateparameterandtheargumentisanlvalu E,thetype "Lvaluereferencetoa" isusedinplaceofafortypededuction. (14.8.2.1.3)
The general meaning is: The derivation of template parameter is actually the comparison and match of formal parameter and argument, if the formal parameter is a reference type (such as p&), then use p to do type derivation If the formal parameter is a cv-unqualified (no const and volatile decorated) right value reference type (such as p&&), and the argument is a left value (such as an object of type a), the type derivation is done with a& (using the a& instead of a).
Copy Code code as follows:

TEMPLATE<CLASS_TP>VOIDF (_tp&&) {/*dosomething*/}
TEMPLATE<CLASS_TP>VOIDG (const_tp&&) {/*dosomething*/}
intx=123;
f (x);//ok,f () template function parameter is not a const non-volatile right value reference type, argument x is the int type left value, and int& is used for argument derivation, so call f<int&> (int&)
f (456);//ok, argument is a right value, call f<int> (int&&)
g (x); the//error,g () function template parameter is a const right value reference type, and g<int> (constint&&) is invoked, and the right value refers to rule four to know that a const right value reference cannot be bound to a left value, resulting in a compilation error

Understanding the derivation process of template function parameters, it is not difficult to understand the implementation of the Std::move () function, when using the left value (assuming its type is T) as a parameter to call the Std::move () function, the actual instantiation and call is Std::move<t&> (T &), and its return type is t&& This is the process of the left value of the Move () function (in fact, the left value is still left, but is treated as the right value, being "copied home", become nothing).
"description": Bjarnestroustrup, the ancestor of C + + said: "If the Move () function is renamed Rval () may be better, but move () the name has been used for many years (Maybeitwouldhavebeenbetterifmove () Hadbeencalledrval (), Butbynowmove () hasbeenusedforyears.).

7, The complete example
So far, we've learned a lot about right-value references, and here's a complete example of implementing move semantics using the right value reference:
Copy Code code as follows:

#include <iostream>
#include <cstring>
#definePRINT (msg) Do{std::cout<<msg<<std::endl;} while (0)
template<class_tp>structremove_reference{typedef_tptype;};
template<class_tp>structremove_reference<_tp&>{typedef_tptype;};
template<class_tp>structremove_reference<_tp&&>{typedef_tptype;};
Template<class_tp>
Inlinetypenameremove_reference<_tp>::type&&move (_tp&&__t) {
typedeftypenameremove_reference<_tp>::type_up;
Returnstatic_cast<_up&&> (__t);
}
classa{
Public
A (CONSTCHAR*PSTR) {
PRINT ("constructor");
M_data= (pstr!=0?strcpy (Newchar[strlen (PSTR) +1],pstr): 0);
}
A (consta&a) {
PRINT ("Copyconstructor");
M_data= (a.m_data!=0?strcpy (Newchar[strlen (A.m_data) +1],a.m_data): 0);
}
A&operator= (consta&a) {
PRINT ("Copyassigment");
if (this!=&a) {
Delete[]m_data;
M_data= (a.m_data!=0?strcpy (Newchar[strlen (A.m_data) +1],a.m_data): 0);
}
Return*this;
}
A (A&&a): M_data (a.m_data) {
PRINT ("Moveconstructor");
a.m_data=0;
}
A&operator= (a&&a) {
PRINT ("Moveassigment");
if (this!=&a) {
M_data=a.m_data;
a.m_data=0;
}
Return*this;
}
~a () {PRINT ("destructor");d Elete[]m_data;}
Private
Char*m_data;
};
Voidswap (a&a,a&b) {
Atmp (Move (a));
A=move (b);
B=move (TMP);
}
Intmain (intargc,char**argv,char**env) {
Aa ("123"), B ("456");
Swap (A,B);
Return0;
}

The output results are:
Copy Code code as follows:

Constructor
Constructor
Moveconstructor
Moveassigment
Moveassigment
destructor
destructor
destructor

8. Behind-the-scenes Tidbits
The proposal for the introduction of the right value reference by the C++11 standard was proposed by Howardhinnant, and its original proposal N1377 in 02, and had undergone several revisions N1385, N1690, N1770, N1855, N1952, N2118. Including its final version of N2118, Howardhinnant's proposal uses the example of a right value reference directly bound to the left value, and by Howardhinnant, Bjarnestroustrup and Bronekkozicki three October 08 jointly signed the "Abriefintroductiontorvaluereferences" article also has a right value reference directly bound to the left value example, But oddly enough, the latest C++11 standard document, published in 11, does not allow right-value references to bind directly to left values. The reasons for this are unknown, but it is not difficult to understand why the earlier compiler version (such as g++4.4.4) binds the right value reference to the left value, does not report a compilation error, and the latest compiler complains.
In addition, Howardhinnant is the libraryworkinggroup of the C + + Standard Committee (Chairman), the defender of Libcxx and Libcxxabi, and Apple's senior software engineer.
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.