1. Rvalue Reference Background introduced
The inefficiency caused by the generation and copying of temporary objects has been a problem that C + + has been criticized. However, the C + + standard allows the compiler to have complete freedom of temporary object generation, thus developing compiler optimization techniques such as Copy Elision, RVO (including Nrvo), which prevent temporary objects from being produced and copied in some cases. The following is a brief introduction to copy Elision, RVO, which is not interested in this can be skipped directly:
(1) Copy Elision
The copy elision technique is designed to prevent some unnecessary temporary objects from being generated and copied, such as:
struct a { a (int) {} A (const A &) {}}; A = 42;
Theoretically, the above a = 42; The statement will be divided into three steps: The first step is to construct a temporary object of type A by 42, the second is to construct a with the temporary object for the parameter copy, and the third step to refactor the temporary object. If a is a large class, then the construction and destruction of its temporary objects will result in significant memory overhead. We only need an object A, why not construct a directly with the 42 parameter? Copy elision Technology is doing this optimization.
"description": You can add a print statement in the copy constructor of a to see if there is a call, and if not, congratulations, your compiler supports copy Elision. However, it should be explained that: a copy of the constructor is not called, but its implementation can not have access rights, do not believe you put it in private permissions to try, the compiler will definitely error.
(2) Return value optimization (Rvo,return value optimization)
The return value optimization technique is also designed to prevent some unnecessary temporary objects from being generated and copied, for example:
struct a { a (int) {} A (const A &) {}}; A get () {return a (1);} A = Get ();
In theory, the above is a = get (); the statements are executed separately: first create a temporary object in the Get () function (assuming TMP1), then construct the return value with TMP1 as the parameter copy (assuming TMP2), and then construct a with the TMP2 as the parameter copy. It is also accompanied by the destruction of TMP1 and TMP2. If a is a large class, then the construction and destruction of its temporary objects will result in significant memory overhead. The return value optimization technique is used to solve this problem, which avoids the generation and copying of two temporary objects for TMP1 and TMP2.
"description": a) You can add a print statement in the copy constructor of a to see if there is a call, and if not, congratulations, your compiler supports the return value optimization. However, it should be explained that: a copy of the constructor is not called, but its implementation can not have access rights, do not believe you put it in private permissions to try, the compiler will definitely error.
b) In addition to the return value optimization, you may also have heard of an optimization technique called named return value optimization (Named return of Value Optimization,nrvo), which, from the programmer's point of view, is actually the same logic as Rvo. Only its temporary object has a variable name identifier, such as modifying the Get () function above as:
A Get () { a TMP (1);//#1 //Do something return tmp;} A = Get (); #2
Think about how many object constructs a type has had since the above modification? Although it looks like there is a display in the structure, #2处看起来也有一次显示地构造, but if your compiler supports Nrvo and copy Elision, you will find the entire A = 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, at a = Get (), and then print the address of a after the statement, you will find that the two addresses are the same, which is the result of applying the NRVO technique.
(3) creation and copying of temporary objects that cannot be avoided by copy Elision, Rvo
While techniques such as copy elision and NVO (including Nrvo) prevent the generation and copying of some temporary objects, they do not work in some cases, such as:
Template <typename t>void Swap (t& A, t& b) { T tmp (a); A = b; b = tmp;}
We just want to swap the data owned by A and b two objects, but have to use a temporary object, TMP, to back up one of the objects, and if the T-type object has data that points (or references) from the heap memory, the memory overhead of the deep copy is conceivable. To this end, the C++11 Standard introduces an rvalue reference, which allows the copy of the temporary object to have the move semantics, so that the copy of the temporary object has a shallow copy-like efficiency, which can be used to some extent to resolve the efficiency of the temporary object's deep copy.
2. Left and right values in the C++03 standard
To understand rvalue references, you first have to differentiate between lvalue (Lvalue) and rvalue (rvalue) values.
The c++03 standard divides an expression into lvalue and rvalue, and is "not left or right":
every expression is either a lvalue or an rvalue.
The easiest way to distinguish whether an expression is an lvalue or an rvalue is to see if it can take an address: if so, it is the lvalue; otherwise, it is the right value.
"description": Due to the introduction of rvalue reference, the classification of expressions in the C++11 standard is no longer "non-left-right" so simple, but in order to understand briefly, we only need to distinguish the left-hand value of the right value can be, the C++11 standard in the classification after the description.
3. Binding rules for rvalue references
Rvalue references (rvalue reference,&&) are similar to traditional references (reference,&), and in order to better differentiate between them, references in the traditional sense are referred to as lvalue references (lvalue reference). The following is a simple summary of the binding rules for Lvalue and rvalue references (the function type object is subject to the exception):
(1) A non-const lvalue reference can only be bound to a non-const left value;
(2) A const lvalue reference can be bound to a const lvalue, a non-const lvalue, a const right value, a non-const right value;
(3) A non-const rvalue reference can only be bound to a non-const right value;
(4) A const rvalue reference can be bound to a const right value and a non-const right value.
The test examples are as follows:
struct a {a () {}}; A Lvalue; Non-Const Lvalue Object const A Const_lvalue; Const Lvalue Object A rvalue () {return A ();} Returns a non-const rvalue Object const a Const_rvalue () {return a ();} Returns a const Rvalue object//Rule one: non-const lvalue references can only be bound to non-const lvalue a &lvalue_reference1 = Lvalue; OkA &lvalue_reference2 = Const_lvalue; Errora &lvalue_reference3 = Rvalue (); Errora &lvalue_reference4 = Const_rvalue (); error//Rule two: A const lvalue reference can be bound to a const lvalue, a non-const lvalue, a const right value, A non-const right-valued const A &const_lvalue_reference1 = Lvalue; Okconst A &const_lvalue_reference2 = const_lvalue; Okconst A &const_lvalue_reference3 = Rvalue (); Okconst A &const_lvalue_reference4 = Const_rvalue (); ok//Rule Three: non-const rvalue references can only be bound to non-const rvalue a &&rvalue_reference1 = Lvalue; Errora &&rvalue_reference2 = Const_lvalue; Errora &&rvalue_reference3 = Rvalue (); OkA &&rvalue_reference4 = Const_rvalue (); Error//Rule four: A const rvalue reference can be bound to a const rvalue and a non-const rvalue, and cannot be bound to an lvalue const A &&const_rvalue_reference1 = Lvalue; Errorconst A &&const_rvalue_reference2 = const_lvalue; Errorconst A &&const_rvalue_reference3 = Rvalue (); Okconst A &&const_rvalue_reference4 = Const_rvalue (); ok//Rule five: function type exception void Fun () {}typedef decltype (fun); typedef void Fun (); Fun & lvalue_reference_to_fun = fun; Okconst Fun & const_lvalue_reference_to_fun = fun; Okfun && rvalue_reference_to_fun = fun; Okconst fun && const_rvalue_reference_to_fun = fun; Ok
"description": (1) Some support rvalue reference but the lower version of the compiler may allow rvalue reference binding to an lvalue, for example, g++4.4.4 is allowed, but g++4.6.3 is not allowed, clang++3.2 is not allowed, said VS2010 beta version allows, the official version is not allowed, I have no VS2010 environment, has not been tested.
(2) Rvalue references bound to literal constants also conform to the above rules, for example: int &&RR = 123, where the literal value 123, although called a constant, can be of type int instead of const int. For this c++03 standard document 4.4. Section 1 and its footnotes are described below:
If T is a non-class type, the type of the rvalue is the cv-unqualified version of T.
In C + + class Rvalues can have cv-qualified types (because they is objects). This differs from ISO C, in which non-lvalues never has cv-qualified types.
So 123 is a non-const rvalue, int &&rr = 123, and the statement conforms to rule three above.
4. Classification of expressions in C++11 standard
The introduction of rvalue references makes the classification of expressions in the C++11 standard no longer a non-lvalue, or rvalue, as simple as the classification of expressions in the C++11 standard:
A simple explanation is as follows:
(1) The lvalue is still the left value in the traditional sense;
(2) Xvalue (expiring value) literal meaning can be understood as the end-of-life value, which is the value of some expression involving rvalue references (an xvalue is the result of the certain kinds of expressions Inv Olving rvalue references), for example, call a return value of a function that returns a return type to an rvalue reference is xvalue.
(3) Prvalue (pure rvalue) literal meaning can be understood as a pure right value, also can be considered as the traditional right value, such as temporary objects and literal value.
(4) Glvalue (generalized value) generalized lvalue, including traditional lvalue and Xvalue.
(5) Rvalue in addition to the traditional sense of the right value, but also includes Xvalue.
The above Lvalue and Prvalue, respectively, are consistent with the traditional concept of lvalue and rvalue, and are more explicit, and describe Xvalue as "some of the values of expressions involving rvalue references". The C++11 standard gives four distinct cases of xvalue:
[Note:an expression is an xvalue if it is:--the result of calling a function, whether implicitly or explicitly, whose Return type is an rvalue reference to object type,--a cast to an rvalue reference to object type,--a class member A ccess expression designating a non-static data member of Non-reference type in which the object expression was an Xvalue, O R--A. * Pointer-to-member expression in which the first operand are an xvalue and the second operand are a pointer to dat A member. In general, the effect of this rule was, named Rvalue references are treated as lvalues and unnamed rvalue references T o objects is treated as xvalues; Rvalue references to functions is treated as lvalues whether named or not. --end Note [example:struct A {int m; }; a&& operator+ (A, a); a&& f (); A; a&& ar = static_cast<a&&> (A); The expressions F (), f (). M, Static_cast<a&&> (a), and A + A are xvalues. The ExpresSion AR is an lvalue.--end example]
It is simply understood that a named Rvalue reference (named Rvalue reference) belongs to an lvalue, an unnamed rvalue reference (unamed rvalue reference) is Xvalue, and an rvalue reference of a reference function type is treated with or without a name as an lvalue. An example is easier to understand:
A rvalue () {return a ();} A &&rvalue_reference () {return a ();} Fun (); Returns an unnamed rvalue reference, belonging to Xvaluea &&ra1 = Rvalue (); Ra1 is a named Rvalue application and belongs to lvalue a &&ra2 = RA1; ERROR,RA1 is treated as lvalue, so RA2 cannot bind to RA1 (not conforming to rule three) A &la = RA1; OK, non-const lvalue reference can be bound to non-const lvalue (conforms to rule i)
5. Move semantics
Now, we re-look at 1-(3), which mentions move semantics, so how do you make a copy of a temporary object have move semantics? Let's take the implementation of a class as an example:
Class A {public: a (const char *PSTR = 0) {m_data = (pstr! = 0? strcpy (new Char[strlen (PSTR) + 1], pstr): 0);}
Copy constructor A (const a &a) {m_data = (A.m_data! = 0? strcpy (new Char[strlen (A.m_data) + 1], A.m_data): 0 ); }
Copy assigment a &operator = (const A &a) { if (this = &a) { delete [] m_data; M_data = (A.m_data! = 0? strcpy (new Char[strlen (A.m_data) + 1], A.m_data): 0); } return *this; }
Move Constructor A (a &&a): M_data (a.m_data) {a.m_data = 0;}
Move assigment 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 above example, in addition to the traditional copy construction (copy constructor) and copy assignment (copy assigment), we have also added the move copy construction (move constructor) and move assignment for the Class A implementation (move assigment). Thus, when we copy a (rvalue) temporary object of Class A, we use a moving copy constructor with the move semantics to avoid the call of the strcpy () function in the deep copy, and when we assign a Class A (right value) temporary object to another object, A move assignment with the move semantics is used to avoid the invocation of the strcpy () function in the copy assignment. This is called the move semantics.
6. Implementation of the Std::move () function
To understand the move semantics, then look at the efficiency issue in 1-(3):
Template <typename t>//If T is Class Avoid swap (t& A, t& b) { T tmp (a); According to the binding rules referenced by Rvalue, the move constructor is not called here, but the copy constructor a = B is called; According to the binding rules referenced by rvalue, the move assigment is not called here, but the copy assigment B = tmp is called; According to the binding rules referenced by rvalue, the move assigment is not called here, but copy assigment} is called
As you can see from the example above, although we have implemented move constructor and move Assigment, the swap () function example still uses the traditional copy constructor and copy assigment. To get them to really use the copy and copy of the move semantics, the Std::move () function comes up and looks at the following example:
void swap (a &a, a &b) { a TMP (Std::move (a));//Std::move (a) is an rvalue, where move constructor a = Std::move (b) is called;
//Std::move (b) is the right value, where move assigment b = Std::move (TMP) is called; Std::move (TMP) is the right value, and move Assigment} is called here
We can not help but ask: we use the right value to apply the binding rules three and four, we know that the rvalue reference cannot be bound to the left value, but how the Std::move () function is the above-mentioned lvalue A, B and TMP into the right value? This is going to start with the implementation of the Std::move () function, in fact, the implementation of the Std::move () function is very simple, following the implementation in the Libcxx library (in the <type_trait> header file) as an example:
Template <class _tp>inline typename remove_reference<_tp>::type&& Move (_tp&& __t) { typedef typename Remove_reference<_tp>::type _up; Return static_cast<_up&&> (__t);}
Where Remove_reference is implemented as follows:
Template <class _tp> struct remove_reference {typedef _TP type;}; Template <class _tp> struct remove_reference<_tp&> {typedef _TP type;}; Template <class _tp> struct remove_reference<_tp&&> {typedef _tp type;};
From the implementation of the move () function, you can see that the parameter (Parameter) type of the move () function is an rvalue reference, how can it bind to lvalue A, B, and TMP as arguments (Argument)? This is not a binding rule that still does not conform to the right value app three! To put it simply, if move is just a normal function (not a template function), then according to the binding rule three and rule four applied by the right value, it is true that it cannot use the lvalue as the fact argument. But it is a template function, which involves the derivation of template parameters, it is different. C++11 standard document 14.8.2.1, the derivation of the template function parameters is described as follows:
Template argument deduction is do by comparing all function template parameter type (call it P) with the type of the CO rresponding argument of the call it A) as described below. (14.8.2.1.1)
If p is a reference type, the type referred to by P are used for type deduction. If P is an rvalue reference to a cvunqualified template parameter and the argument are an lvalue, the type "Lvalue Referenc E to a "are used in place of a for type deduction. (14.8.2.1.3)
The general meaning is: the derivation of the template parameter is actually the comparison and match of the formal parameter and the actual parameter, if the formal parameter is a reference type (such as p&), then use p to do the type deduction If the parameter is an rvalue reference type (such as p&&) with a cv-unqualified (no const and volatile modifiers), and the argument is an lvalue (such as an object of type a), the type deduction is done with a& (using a& instead of a).
Template <class _tp> void f (_tp &&) {/* do something */}template <class _tp> void g (const _TP && amp;) {/* do something */}int x = 123;f (x); The ok,f () template function parameter is a non-const non-volatile rvalue reference type, argument x is an int type lvalue, and int& is used for argument derivation, so call F<int &> (int &) f (456); Ok, argument is rvalue, call f<int> (int &&) g (x); The Error,g () function template parameter is a const rvalue reference type, which invokes g<int> (const int &&), which is known by the rvalue reference rule four, that a const rvalue reference cannot be bound to an lvalue, and therefore causes a compilation error
Understanding the derivation of template function parameters is not difficult to understand the implementation of the Std::move () function, when using an lvalue (assuming its type is T) as a parameter call Std::move () function, the actual instantiation and call is Std::move<t&> (T &), and its return type T&& This is the procedure for the left value of the Move () function (the left value itself is still left, but is treated as an rvalue, and is "copied home" and becomes nothing).
Description: The move () function may be better renamed Rval (), but the name of Move () has been used for several years (C++faq:maybe it would has been better if move () had been called RVA L (), but is now move () have been used for years.).
7, The complete example
At this point, we have learned a lot of right-value reference points of knowledge, the following gives a complete use of rvalue reference to implement the move semantics example:
#include <iostream> #include <cstring> #define PRINT (msg) do {std::cout << msg << Std::endl;} whil E (0) template <class _tp> struct remove_reference {typedef _tp type;}; Template <class _tp> struct remove_reference<_tp&> {typedef _tp type;}; Template <class _tp> struct remove_reference<_tp&&> {typedef _tp type;}; Template <class _tp>inline typename remove_reference<_tp>::type&& Move (_tp&& __t) {Typede F TypeName Remove_reference<_tp>::type _up; Return static_cast<_up&&> (__t);} Class A {public:a (const char *pstr) {PRINT ("constructor"); M_data = (Pstr! = 0? strcpy (new Char[strlen (PSTR) + 1], pstr): 0); } A (const a &a) {PRINT ("copy constructor"); M_data = (A.m_data! = 0? strcpy (new Char[strlen (A.m_data) + 1], A.m_data): 0); } A &operator = (const A &a) {PRINT ("copy assigment"); if (This! = & a) {delete [] m_data; M_data = (A.m_data! = 0? strcpy (new Char[strlen (A.m_data) + 1], A.m_data): 0); } return *this; } A (a &&a): M_data (a.m_data) {PRINT ("move constructor"); A.m_data = 0; } A & operator = (a &&a) {PRINT ("Move Assigment"); if (this = &a) {m_data = A.m_data; A.m_data = 0; }return *this; } ~a () {PRINT ("destructor"); Delete [] m_data;} Private:char * m_data;}; void swap (a &a, a &b) {a TMP (move (a)); A = Move (b); b = Move (TMP);} int main (int argc, char **argv, char **env) {A A ("123"), B ("456"); Swap (A, b); return 0;}
The output is:
Constructorconstructormove Constructormove Assigmentmove Assigmentdestructordestructordestructor
8. Trivia
The proposal for the introduction of rvalue references by the C++11 standard was proposed by Howard Hinnant, whose original proposal was N1377 in 02 and underwent several modifications N1385, N1690, N1770, N1855, N1952 and N2118. Including the final version of N2118, Howard Hinnant's proposal used an example of an rvalue reference directly bound to an lvalue, and was co-signed by Howard Hinnant, Bjarne Stroustrup and Bronek Kozicki in October 08. The Brief Introduction to Rvalue References article also has an example of an rvalue reference directly bound to an lvalue, but it is strange that an rvalue reference is not allowed to be bound directly to an lvalue in the C++11 standard document, where the cause is unknown. However, it is not difficult to understand why earlier compiler versions (such as g++ 4.4.4) allow rvalue references to be bound to lvalue, while the latest compiler will give an error.
Also, introduce the standard library of Howard Hinnant and its maintenance: Howard Hinnant is the manager of the C + + Standards Committee Library Working Group, Libcxx and Libcxxabi, and Apple's senior software engineer. Libcxx Libraries use rvalue references in large numbers and want to learn more about the application instances of rvalue references, you can look at the Libcxx code.
Reference Documentation:
ISO/IEC 14,882:2011
A Brief Introduction to Rvalue References
C++11 FAQ
Http://www.cnblogs.com/soaliap/archive/2012/11/19/2777131.html
C++11 Standard rvalue Reference (rvalue reference)