A few days ago read an article, "4 lines of code to see the right value of reference" think it is well written, but feel that the right value of the reference to a lot of the content can be dug to learn, so summed up a bit, I hope to have a deeper understanding of rvalue reference
Oneseveral basic concepts
1.1 left and right values
The distinction between Lvalue and rvalue is the ability to get an address.
In the earliest C + +, the definition of an lvalue represents an expression that can get an address, which can appear to the left of an assignment statement and assign a value to that expression. But the presence of the modifier const makes it possible to declare the following identifier, which can take the address, but there is no way to assign it:
Const int ten;
An rvalue indicates an object that cannot get an address, with a constant value, a function return value, a lambda expression, and so on. The address cannot be obtained, but it does not indicate that it is immutable, and you can change the right value when you define an rvalue reference to an rvalue.
1.2 lvalue Reference and Rvalue reference
A traditional C + + reference is referred to as an lvalue reference and is used as follows:
Ten& II = I;
In C + + Primer Plus version 6th 18.1.9, when an rvalue reference is added to the c++11, an rvalue reference is associated to the right value, the right value is stored in a specific location, and the Rvalue reference points to that particular location, that is, the right value cannot get the address, but the rvalue reference is the one that can get the address. The address represents the location where the temporary object is stored. The syntax is as follows:
int ten;
1.3
assembly code for Lvalue references and rvalue references
The following compilations are x86 compiled
Write a simple sentence to see its assembly
int 1 ; int & II = i;
0x080483f3 movl $0x1,-0x10 (%EBP) 0x080483fa Lea -0x10 (%EBP),% EAX0X080483FD mov %eax,-0x8 (%EBP)
The first sentence is to assign a value of 1 to I, the second sentence puts the address of I into EAX, and the third sentence passes the value in EAX to II. A visible reference is the address of a variable obtained from a variable and assigned to a reference variable.
Another look at the compilation of rvalue references
int ten;
0x08048400 mov $0xa,%eax0x08048405 mov %eax,-0xc (%EBP) 0x08048408 Lea -0xc (%EBP),%eax0x0804840b mov %eax,- 0x4 (%EBP)
The first sentence assigns a value of 10 to eax, the second sentence puts eax into -0xc (%EBP), and says "The temporary variable refers to the right value, the right value is stored in a specific location", in this program, -0XC (%EBP) is the address of the temporary variable, The latter two sentences are stored at III by EAX.
Through the above code, we can also find that in the above program -0x4 (%EBP) holds the Rvalue reference iii,-0x8 (%EBP) holds the Lvalue reference, -0XC (%EBP) stored 10, and -0x10 (%EBP) stored 1, Lvalue and rvalue references are four bytes as int (because they are addresses)
At the same time, we can understand the temporary variable in depth, in this program, the name of the 1 (name is i) and no name of the 10 (temporary variable) value is actually handled in the same way, that is, the temporary variable is essentially a variable without a name. Its life cycle and function stack frames are consistent. It can also be said that the temporary variable and its reference have the same life cycle.
1.4 Const
Lvalue Reference
If you write the following code, define an lvalue reference and set its value to a constant value, you will get an error:
int ten;
The reason is obvious, the left is an lvalue reference, and the right is an rvalue, and the Lvalue reference cannot be bound to a right value.
However, if it is a const lvalue reference, it can be bound to the right value. That is, the following wording is in accordance with the grammatical specification:
Const int ten;
The assembly code for this procedure is as follows:
0x08048583 mov $0xa,%eax0x08048588 mov %eax,-0x8 (%EBP) 0x0804858b Lea -0x8 (%EBP),%eax0x0804858e mov %eax,-0x4 ( %EBP)
The -0x4 (%EBP) is stored in i,-0x8 (%EBP) where the temporary object 10 is stored, and the program stores 10 of the address at I. You see here that there is no difference between a const reference and an rvalue reference when it is bound to the right value.
1.5 reciprocal assignment of lvalue and Rvalue references
An rvalue reference can be assigned to an lvalue reference, the Lvalue reference is bound to the object pointed to by the Rvalue reference, in earlier C + +, the reference has no left and right points, the introduction of an rvalue reference is called an lvalue reference, so the Lvalue reference can actually bind any object. This also allows you to understand why a const lvalue reference can give a constant value.
int Ten ; int& II = III; // II is equal to 10, and the change to II also acts on the III
Second, rvalue reference and move semantics
In the old C + +, there are a lot of unnecessary copies, because in some cases the objects are destroyed after they have been copied. The new standard introduces a move operation that reduces a lot of replication operations, while rvalue references a new reference type that is formally introduced to support the move operation.
2.1 Standard library Move
function
According to the syntax rules referenced by rvalue, you cannot bind an rvalue reference to an Lvalue, C++11 introduces an rvalue reference, and provides a move function to obtain an rvalue reference bound to an lvalue, which is defined in the header file utility.
Int &&III = Move (ii)
After you call move, you must guarantee that we will no longer use it except to copy or destroy it, and after we call move, we cannot make any assumptions about the object after moving the source.
2.2 template argument inference and referencing
To understand the implementation of the move function, you first need to understand the template argument inference and references.
When an lvalue reference is used as a parameter, look at a few examples:
template<classvoid F1 (t&) {}f1 (i) //i is an int, template parameter type T is int F1 (CI) //CI is a const int, the template parameter T is a const intFL (5) /// Error: passed to A & Parameter argument must be an lvalue
If the parameter of the function is a const reference:
template<classvoid F2 (const t&) {}f2 (i) //i is an int, The template parameter type T is int, because non-const can be converted to constF2 (CI) //CI is a const int, the template parameter T is intF2 (5) // look ahead, the const reference can be bound to the right value, T is int
When a parameter is an rvalue reference,
template<classvoid f3 (t&&) {}f3 (5) // T is int
2.3 Referencing collapsed and rvalue reference parameters
As a rule, F3 (i) should be incorrect, because an rvalue reference cannot be bound to an lvalue, but there are two exceptions to normal binding rules in C + + that allow this binding. These two exception rules are the basis for a move to work correctly
Exception 1 : The type inference for rvalue references. When we pass an lvalue to the rvalue reference of a function as a parameter (the function argument is t&&), the compiler infers that the template type argument is an lvalue reference type of the argument, so when you call F3 (i), T is inferred as int&, not int. Also, changes to the parameters in the template function are reflected in the arguments passed in at the time of the call.
In general, we cannot directly define a reference, but it is possible to define the same over-type aliases (using typedef) and templates indirectly.
Exception 2 : reference collapsed. When referenced references are defined, these references form "collapsed", and in all cases (except one exception), the references are collapsed into a normal lvalue reference type. This exception is an rvalue reference to an rvalue reference:
L x& &&, x& &&, x&& & all folded into x&
L type x&& && folding into x&&
2.4 Understanding Rvalue reference collapsed and Rvalue reference type inference
For function F3, the following results can be known based on rvalue reference type inference rules :
F3 (i) // arguments are lvalue, template parameter T is int&f3 (CI) // argument is lvalue, template parameter T is a const int&
But when T is inferred as int&, the function F3 is instantiated like this:
void f3<int&> (int& &&)
Then, depending on the rvalue reference collapse rule , the above instantiation should be collapsed to look like this:
void f3<int&> (int&)
These two rules lead to two important results:
L If a function argument is an rvalue reference to a template type parameter, such as T&&, then it can be bound to an lvalue, and
L If the argument is an lvalue, the Inferred template argument type is referred to as an lvalue, and the function argument is instantiated as a normal lvalue reference parameter (t&)
It is worth noting that the parameter is t&& a function of type can accept all types of parameters, and Lvalue right values are available. As mentioned earlier, the const lvalue refers to the function that makes the argument and accepts all types of arguments as well.
2.5 When an rvalue reference is used as a function template parameter
By preceding, we learned that when an rvalue reference is used as a function template parameter, the type T is inferred as a reference type. This feature affects the code inside the template function, looking at the following code:
template<class t>void f3 (t&& val) { = val; = FCN (t); if (val = = t) {...}}
If the function is called with lvalue I, then T is inferred as Int&, and T is bound to Val, and the change to T is applied to Val, then the if judgment condition is always true.
Rvalue references are typically used in two cases where the template is forwarded in fact, and the template is overloaded. The following will describe
As mentioned earlier, the const lvalue reference makes arguments and rvalue references as parameters, which can match all parameter types, but when overloaded functions occur simultaneously, the Rvalue reference does not have a const rvalue to the function that makes the argument, The const lvalue refers to the function-bound lvalue and const-rvalue that make the argument (the non-const rvalue is the right value referenced by an rvalue reference, although the address of the right value cannot be obtained, but you can change the rvalue by defining an rvalue reference):
template<classvoid F (t&&) // bound to non-const rvalue template<class void F (const t&) // left value and const right value
2.6 Move
function Implementation
The move function in vs2017 is defined as follows
usingremove_reference_t = TypeName Remove_reference<_ty>:: Type;template<class_ty>constexpr remove_reference_t<_Ty>&& Move (_ty&&_arg) _noexcept{return(static_cast<remove_reference_t<_ty>&&>(_arg));} Template<class_ty>structremove_reference{//Remove ReferenceusingType =_ty;}; Template<class_ty>structRemove_reference<_ty&>{ //Remove Reference usingType =_ty;}; Template<class_ty>structRemove_reference<_ty&&>{ //Remove rvalue Reference usingType =_ty;};
Using rvalue references as arguments, as mentioned earlier, can match all types. The following two ways are correct:
String= Move (string("bye!")) // _ty inferred to be strings2 = Move (S1) //_ty inferred as string&
As for Remove_reference, it's a good idea.
In summary, you can find that the move function returns its rvalue reference type regardless of the type parameter passed in, whether it is an lvalue or an rvalue.
Third, forwarding
Some functions require that one or more of these arguments be forwarded to other functions along with the type, in which case we need to maintain all the properties of the forwarded arguments, including whether the arguments are const, as well as an lvalue or an rvalue.
Like the next two functions, call F in flip:
void f (intint &v2) { "" << ++v2 << <typename F, TypeName T1, TypeName t2>void flip (f F, T1 T1, T2 T2) { f (T2, T1);}
We'll find that f will change the value of the second parameter, but after the flip call F it won't change
F (a)
The template is instantiated as follows:
void Flip (void(*FCN) (intint. int int T2);
The value of j is copied to the T1, so the F in flip only changes the T1 without changing the J
3.1 Defining function parameters that can persist type information
If you define flip's parameters as rvalue references, according to the rules described above, when you pass a reference to flip, T1 is inferred as int&,t1 and is folded into int&, which perfectly preserves the type of the argument.
Template <typename F, TypeName T1, TypeName t2>void Flip (f F, t1&& T1, t2&& T2) { F (T2, T1);}
But when function f accepts an rvalue reference as a parameter, flip does not work properly.
void f (intint j) { << i << "" << J << Endl;} Flip (g,i) // error, cannot instantiate from an lvalue int&&
Note that both F and g are not template functions, so the two exceptions mentioned earlier as arguments are not valid. So this is wrong.
3.2 Using forward to persist type information
Forward needs to display the supplied argument type, returning an rvalue reference of that argument type (as you can see earlier, an rvalue reference can be assigned to an lvalue reference)
void Flip (f F, t1&& T1, t2&& T2) { F (forward<T2> (T2), forward<t1 >(T1));}
When an explicit argument T is int&&, forward returns int&& &&, and collapses to int&&. When the explicit argument T is int&, forward returns int& &&, which is collapsed into int&. So forward perfectly maintains the type of the parameter.
C + + rvalue references and using