当前位置:网站首页>Summary of left value, right value, left value reference and right value reference in [C + + 11]

Summary of left value, right value, left value reference and right value reference in [C + + 11]

2020-12-07 17:29:23 osc_ tnexgcb8

Left and right

The left value : A persistent object that exists after the end of an expression , You can take the address , A named variable or object
Right value : Temporary objects that no longer exist after the end of an expression , You can't take the address , Isn't there a name .
such as int a = b + c;,a It's a left value , It can be done to a Address fetch , and b+c It's a right value , For expressions b+c If you take the address, you will report an error .C++11 The middle right value consists of two concepts : The dead value and the pure right value .

Pure right value and will die value

stay C++98 in , Right value is pure right value , Pure right values refer to the values of temporary variables 、 The value of the object is not literal . Include non referenced function return values 、 Expression etc. , such as 2、‘ch’、int func() etc. . Will die value is C++11 Newly added 、 The expression associated with the right-hand reference .

  • Pure right value : Temporary variable returned by non reference ( int func(void) )、 A temporary variable generated by an operational expression (b+c)、 The original literal amount (2)、lambda Expression etc. .
  • Dead value : The object to be moved 、T&& Function return value 、std::move Return value and convert to T&& The return value of the conversion function of the type of .

Dead value can be understood as passing through “ Stealing ” Other variable memory space way to get the value . To ensure that other variables are no longer used 、 Or about to be destroyed , adopt “ Stealing ” Can avoid the release and allocation of memory space , It can extend the lifetime of variable values .

Right value reference and left value reference

Introduce

An R-value reference is a type of reference to an R-value , Marked as T&&. Because the right value is not named , It's found by reference , To express by reference , Right value references are also references to references ( That's what I think at the moment ).

An lvalue reference is a type of reference to an lvalue .

The reference itself does not own the memory of the bound object , The object is just an alias , An lvalue reference is an alias for a named variable , An R-value reference is an alias for an unnamed variable . Therefore, both the lvalue reference and the rvalue reference must be initialized immediately .

Reference by right value , The dying right value is “ A new lease on life ”, Its life cycle is the same as that of the right-hand reference type variable , As long as the variable of the right-hand reference type is alive , Then this right value temporary quantity will always be alive , This is an important feature , You can take advantage of this for some performance optimizations , Avoid copy construction and destruction of temporary objects .

L-value references include Constant lvalue reference and Non constant l-value reference . Non constant lvalue references can only accept lvalues , Right values cannot be accepted ; A constant lvalue reference is a “ universal ” The type of reference , L-values are acceptable ( Constant left value 、 Non constant left value )、 Right value . However, the right value referred to by the left value of a constant is in its “ The rest of my life ” It can only be read-only .

int &a = 2;       //  Non constant l-value reference   Bound to the   Right value , Compile failed 

int b = 2;        // b  It's a non constant left value 
const int &c = b; //  Constant lvalue reference   Bound to the   Non constant left value , Compile and pass 

const int d = 2;  // d  It's a constant l-value 
const int &e = d; //  Constant lvalue reference   Bound to the   Constant left value , Compile and pass 
const int &f =2;  //  Constant lvalue reference   Bound to the   Right value , Compile and pass 

An R-value reference cannot usually be bound to any lvalue , To bind an lvalue to an rvalue reference , Usually std::move() Cast an l-value to an R-value . such as :

int a;
int &&r1 = a;             //  Compile failed 
int &&r2 = std::move(a);  //  Compile and pass 

Right value reference features

Right value references are independent of left and right values . It means that the variable of right reference type may be left value or right value . such as :

int&& val1 = x;

var1 The type is an R-value reference , but var1 It's an lvalue , Because named variables are all lvalues .

T&& It doesn't necessarily mean right value , The type of binding is undefined , It can be either left-handed or right-handed .

template<typename T>
void f(T&& param){}

f(10); //param It's right value 

int x = 10;
f(x); //param It's left 

T&& param, Express param It's actually an undetermined reference type , be called universal references, You can think of it as an undetermined reference type , It has to be initialized , Whether it is an lvalue or an rvalue reference depends on its initialization , If && Initialized by an lvalue , It's an l-value ; If it is initialized with a right value , It's a right value .
The above example , When the parameter is the right value 10 When , according to universal references Characteristics ,param Initialized by a right value , that param Right value ; When the parameter is an lvalue x when ,param Initialized by an lvalue reference , that param It's a left value .

Only when automatic type inference occurs ( For example, the type of function template is automatically derived , or auto keyword ),T&& It's a universal references. such as

template<typename T>
void func(T&& param)   //T Need to deduce ,&& It's a universal references

template<typename T>
class Test {
    Test(Test&& rhs); 
};

In the example above ,param yes universal references,rhs yes Test&& Right quoting , Because the template function func Type inference occurred , and Test&& There is no type derivation , because Test&& It's a definite type .

Let's do another example :

template<typename T>
void func(const T&& param);

In the example above param No universal references, It's actually an R-value reference .universal references Only in the T&& Under the occurrence of , Any additional condition will make it invalid , So it was const After modification, it becomes a right value reference .

Reference folding rules

Derived by type T&& type , Compared with the right-hand reference (&&) There will be a type change , This change is called reference collapse or collapse . The rules :

  • All the right-hand references that are superimposed on the right-hand reference are still a right-hand reference ;
  • The overlay between all other reference types becomes an lvalue reference .

In short , All the right-hand references superimposed on the right-hand reference are still a right-hand reference , Other reference collapses are LVS .

T& &、T& && and T&& & All folded into types T&
T&& && Fold it into T&&

Right value function template parameter type derivation

template<typename T>
void foo(T&&);
 -

1. When to foo When the parameter of the function is an l-value , for example :

int i = 29;
foo(i);//i Quote... For left 
 here ,T The type of int The left quotation of :int&, Parameter type is int& &&,( namely T&&), Combined with the reference folding rule above , The type of the final parameter is int The left quotation of :int&. 

2. When to foo When the parameter of the function is a right value , for example :

foo(29);
 here ,T The type of int, Parameter type is int&&,( namely T&&).

Still understanding ing, It's complicated , I can't use it , Come here first .

And a little bit more , Forget to add .
The compiler treats named rvalues as lvalues , The unnamed right-hand reference is treated as a right-hand value .

#include <iostream>
#include <string>
#include <stack>
#include <vector>
#include <algorithm>
#include <cstdio>

using namespace std;

void PrintValue(int & i)
{
    cout << "lvalue : " << i << endl;
}

void PrintValue(int&& i)
{
    cout << "rvalue : " << i << endl;
}

void Forward(int&& i)
{
    PrintValue(i);
}

int main()
{
    int i = 0;
    PrintValue(i);
    PrintValue(1);
    Forward(2);

    return 0;
}

Output results :

 Picture description here

Forwaid Function receives a right value , Forward to PrintfValue Then it turns to l-value again , stay Forward Call in PrintfValue when , Right value i It becomes a named object , The compiler treats it as an lvalue .

版权声明
本文为[osc_ tnexgcb8]所创,转载请带上原文链接,感谢
https://chowdera.com/2020/12/20201207172217055y.html