当前位置:网站首页>Explain left value, right value, left value reference and right value reference in detail
Explain left value, right value, left value reference and right value reference in detail
2022-07-27 04:36:00 【Butayarou】
List of articles
- One 、 Left and right
- Two 、 L-value reference and right value reference
- 3、 ... and 、 Usage scenario and practical significance of lvalue reference
- Four 、 Right quoting
One 、 Left and right
1. The left value
An lvalue is an expression that represents data , such as : Variable name 、 Dereferenced pointer variable . In a general way , We can Get its address and Assign a value to it , But being const Modified lvalue , You can't assign a value to it , But you can still get its address .
Overall speaking , The object that can take the address is the lvalue .
// Following a、p、*p、b It's all l-values
int a = 3;
int* p = &a;
*p;
const int b = 2;
2. Right value
The right value is also an expression that represents data , such as : Literal constants 、 The expression returns the value , Return value of the value passing function ( Is to pass the value and return , Instead of passing references back ), The right value cannot appear on the left of the assignment symbol and cannot take the address .
Overall speaking , The object that cannot get the address is the right value .
double x = 1.3, y = 3.8;
// The following are common right values
10; // Literal constants
x + y; // The expression returns the value
fmin(x, y); // Return value of the value passing function
None of the following can be compiled :
10 = 4;、x + y = 4;、fmin(x, y) = 4;,VS2015 Compiler error :error C2106: “=”: Left operand must be an lvalue . reason : The right value cannot appear to the left of the assignment symbol .&10;、&(x + y);、&fmin(x, y);,VS2015 Compiler error :error C2102: “&” Lvalue required . reason : Right value cannot take address .
3. summary
Distinguish between left and right values , After all, it depends on whether you can get the address .
Two 、 L-value reference and right value reference
Conventional C++ There is reference grammar in grammar , and C++11 The syntax feature of right value reference is added in the standard , So in order to distinguish the two , take C++11 References before the emergence of the standard are called lvalue references .
Whether l-value reference or R-value reference , It's all about aliasing objects .
1. lvalue reference
An lvalue reference is a reference to an lvalue , Alias the lvalue .
// The following are lvalue references to the above lvalue
int& ra = a;
int*& rp = p;
int& r = *p;
const int& rb = b;
2. Right quoting
An R-value reference is a reference to an R-value , Alias the right value .
The expression of right value reference is to add two after the specific variable type name &, such as :
int&& rr = 4;.
// The following are right value references to the above right value
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
Be careful :
Right quotingquoteRight value, Will cause the right value to be stored in a specific location .
in other words , An R-value reference variable is actually an l-value , It can be addressed and assigned (const The right value reference variable can take an address but cannot be assigned , because const It's working ).
Of course , Taking the address refers to taking the address of the variable space ( The right value cannot take the address ).such as :
double&& rr2 = x + y;&rr2;rr2 = 9.4;
Right quoting rr2 Reference right value x + y after , The return value of this expression is stored in a specific location , Cannot get the return value of the expression x + y The address of , But you can take rr2 The address of , You can also modify rr2 .const double&& rr4 = x + y;&rr4;
It can be done to rr4 Address fetch , But it can't be modified rr4, It's written asrr4 = 5.3;Compile and report errors .
Now we know that lvalue references can reference lvalues , An R-value reference can refer to an R-value .
Then whether the left value reference can reference the right value ? Can an R-value reference refer to an l-value ?
The following comparison and summary give the answer .
3. Comparison and summary
Lvalue reference summary :
- Lvalue references can only refer to lvalues , You cannot directly reference the right value .
- however
const lvalue referenceYou can reference lvalues , You can also reference the right value .
// 1. Lvalue references can only refer to lvalues
int t = 8;
int& rt1 = t;
//int& rt2 = 8; // Compiler error , because 10 It's right value , You cannot directly reference the right value
// 2. however const Lvalue references can refer to lvalues
const int& rt3 = t;
const int& rt4 = 8; // You can also reference the right value
const double& r1 = x + y;
const double& r2 = fmin(x, y);
ask : Why?
const lvalue referenceYou can also reference the right value ?
answer : stay C++11 Before the standard came into being , There is no right value to refer to this concept , At that time, if you want a type that can receive both left and right values , Need to useconst lvalue reference, For example, standard containers push_back Interface :void push_back (const T& val).
in other words , Ifconst lvalue referenceYou cannot quote the right value , Some interfaces are not easy to support .
The following is C++98 Relevant interfaces in the standard const lvalue reference An example of quoting an R-value :
vector<int> v;
v.push_back(1);
v.push_back(2);
v.push_back(3);
R-value reference summary :
- The right value reference can only refer to the right value , You cannot directly reference an lvalue .
- But right value references can be referenced by
moveLeft value of .
move, This article refers tostd::move(C++11), The function is to forcibly convert an left value to an right value , To achieve mobile semantics .
The lvalue is move Then it becomes the right value , So the right value reference can reference .
// 1. The right value reference can only refer to the right value
int&& rr1 = 10;
double&& rr2 = x + y;
const double&& rr3 = x + y;
int t = 10;
//int&& rrt = t; // Compiler error , You cannot directly reference an lvalue
// 2. But right value references can be referenced by move Left value of
int&& rrt = std::move(t);
int*&& rr4 = std::move(p);
int&& rr5 = std::move(*p);
const int&& rr6 = std::move(b);
3、 ... and 、 Usage scenario and practical significance of lvalue reference
1. Use scenarios
// 1. Lvalue reference as parameter
void func1(string s)
{
...}
void func2(const string& s)
{
...}
int main()
{
string s1("Hello World!");
func1(s1); // Because it is value transfer and parameter transfer, and it is a deep copy , It costs a lot
func2(s1); // Lvalue reference as parameter reduces copy , Improved efficiency
return 0;
}
// 2. An lvalue reference is used as the return value ( Only when the object is out of the scope of the function )
string s2("hello");
// string operator+=(char ch) Pass the value to return that there is a copy and it is a deep copy
// string& operator+=(char ch) There is no copy of the return value of the lvalue reference , Improved efficiency
s2 += '!';
2. Practical significance
Copy will be generated by value transfer, parameter transfer and value return , Some are even deep copies , The price is very big . The practical significance of lvalue reference is that making parameters and return values can reduce the copy , To improve efficiency .
3. Short board
Lvalue quotation solves most problems perfectly , But some problems still can't be solved well .
When the object still exists after it is out of the function scope , You can use an lvalue reference to return , That's fine .
string& operator+=(char ch)
{
push_back(ch);
return *this;
}
But when the object ( An object is a local object within a function ) When there is no function scope , You can't use lvalue reference to return .
string operator+(const string& s, char ch)
{
string ret(s);
ret.push_back(ch);
return ret;
}
// Take this function as an example :ret Is a local object in a function , After the function scope is out, it will be destructed , It was destroyed
// If its alias is returned at this time ( lvalue reference ), That is to use this object again , There will be problems
therefore , For the second case , Lvalue references can do nothing , You can only pass values and return .
Four 、 Right quoting
therefore , In order to solve the copy problem of the above value transfer return ,C++11 The standard is increased Right quoting and Mobile semantics .
1. Mobile semantics (Move semantics)
Put the resources in an object Move To another object ( Transfer of resource control ).
(1) Mobile construction
① Concept
Transfer Parameter right value Resources to construct yourself .
// This is a simulation string Mobile construction of class implementation
string(string&& s)
:_str(nullptr)
, _size(0)
, _capacity(0)
{
swap(s);
}
Copy constructor and move constructor are overloaded functions of constructor , The difference is :
- The parameters of the copy constructor are const lvalue reference , Receive left or right values ;
- The argument of the mobile constructor is the right value reference , Receive the right value or be move Left value of .
notes : When the parameter passed is a right value , Although the copy constructor can receive , But the compiler will think that the mobile constructor is more matched , The move constructor is called .
in general , If both functions are defined in the class , When constructing objects :
- If the left value is used as a parameter , Then the copy constructor is called , Make a copy ( If it is like string Such classes with resources in heap space , Then every time the copy structure is called, a deep copy will be made ).
- If the right value is used as a parameter , Then the mobile construct will be called , Calling the mobile construct will reduce the copy ( If it is like string Such classes with resources in heap space , Then every time you call the mobile structure, you will make a deep copy less ).
For example, execute the following lines of code :
string s("Hello World11111111111111111");
string s1 = s; // s It's left , So call the copy constructor
string s2 = move(s); // s By move Then it becomes the right value , So call the move constructor ,s Resources will be transferred to construct s2
// It should be noted that ,move Generally, it is not used like this , because s Our resources have been diverted
perform
string s1 = s;front :
performstring s1 = s;after ( It's also executionstring s2 = move(s);front ):
performstring s2 = move(s);after :
② Comparison of mobile structures
For example, execute statements cout << MyLib::to_string(1234) << endl;
Only copy structure, no move structure :
stay to_string Before the function stack frame is destroyed , Use local objects str Copy the temporary object constructed and return to the function call .
There are both copy structures and mobile structures :
stay to_string Before the function stack frame is destroyed , Use local objects str ( Anyway str To destroy , take str As an R-value , Direct transfer str Resources for ) Move to construct a temporary object and return to the function call .
For example, execute statements MyLib::string ret = MyLib::to_string(1234);
Only copy structure, no move structure :
stay to_string Before the function stack frame is destroyed , First use local objects str Copy the temporary object constructed and return to the function call ,to_string After the function stack frame is destroyed , Then use the temporary object copy to construct ret .
But today's compilers generally optimize : Because temporary objects have ret To receive , In this case, the creation and destruction of temporary objects are redundant , Why not omit this step , Direct use str Copy to construct ret .
There are both copy structures and mobile structures :
stay to_string Before the function stack frame is destroyed , Due to local objects str It's left ( You can address it ), So use str Copy the temporary object constructed and return to the function call ,to_string After the function stack frame is destroyed , Because the temporary object is an R-value , So use temporary object movement to construct ret .
But today's compilers generally optimize : Because temporary objects have ret To receive , First copy and construct a temporary object, and then move it to construct ret , Temporary objects seem unnecessary , It's better to omit . since str yes to_string Local object of function stack frame , Finally, we should destroy , It's better to str As an R-value , Direct transfer str Resources are used to construct ret , That is to say, directly use str Move to construct ret .
Another example is to execute the following code :
After calling this function , You need to pass values to return this user-defined type that takes up a lot of resources ,
stay C++98 in , There is no mobile structure , Copy structure to make deep copy , It costs a lot ;
stay C++11 in , Direct mobile structure , Transfer m Resources to ret , Improved efficiency .
(2) Move assignment
① Concept
Transfer Parameter right value Resources to give themselves .
// This is a simulation string The mobile assignment of the implementation of class
string& operator=(string&& s)
{
swap(s);
return *this;
}
Copy assignment function and move assignment function are overloaded functions of assignment operator overloaded functions , The difference is :
- The parameters of the copy assignment function are const lvalue reference , Receive left or right values ;
- The parameter of the mobile assignment function is the right value reference , Receive the right value or be move Left value of .
notes : When the parameter passed is a right value , Although the copy assignment function can receive , But the compiler will think that the mobile assignment function is more matched , Will call the move assignment function .
in general , If both functions are defined in the class , When assigning values to objects :
- If the left value is used as a parameter , Then copy assignment will be called , Make a copy ( If it is like string Such classes with resources in heap space , Then every time the copy assignment is called, a deep copy will be made ).
- If the right value is used as a parameter , Then the move assignment will be called , And calling the move assignment will reduce the copy ( If it is like string Such classes with resources in heap space , Then every time you call the move assignment, you will make one less deep copy ).
For example, the following lines of code :
string s("11111111111111111");
string s1("22222222222222222");
s1 = s; // s It's left , So call the copy assignment function
string s2("333333333333333333");
s2 = std::move(s); // s By move Then it becomes the right value , So call the move assignment function ,s Resources will be transferred to s2
// It should be noted that ,move Generally, it is not used like this , because s Our resources have been diverted
② Comparison of mobile assignment with and without
For example, execute the following statement :MyLib::string ret("111111111111111111111111");ret = MyLib::to_string(12345);
No move assignment ( There are mobile construction and copy assignment ):
use str( Compiler view str Is the right value ) Move and construct a temporary object as the return value , Then copy and assign the temporary object to ret .
There is mobile assignment :
use str( Compiler view str Is the right value ) Move and construct a temporary object as the return value , Because the temporary object is an R-value , Then the temporary object is moved and assigned to ret .
2. Usage scenario of right value reference
In addition to the above scenarios ,C++11 The standard STL The relevant interface functions of the container have also added the right value reference version .
such as :


3. Perfect forwarding (Perfect forwarding)
(1) The reason for introducing
Before that, we need to know what universal quotation is :
Determine the type Of && Indicates the right quotation ( such as :int&& ,string&&),
But in the function template && It does not mean the right value reference , It's a universal reference , The template type must be determined by inference , The receiving The left value It will be deduced as lvalue reference , receive Right value It will be deduced as Right quoting .
Pay attention to distinguish between right value reference and universal reference : Of the following functions T&& Not universal quotation , because T The type of has been determined when the template is instantiated .
template<typename T>
class A
{
void func(T&& t); // When instantiating a template T The type of , When you call a function T Is a definite type , So here is the right value reference
};
Let's understand universal quotation through the following program :
template<typename T>
void f(T&& t) // Omnipotent quotation
{
//...
}
int main()
{
int a = 5; // The left value
f(a); // After passing parameters, the universal reference is deduced as an lvalue reference
const string s("hello"); // const The left value
f(s); // After passing the parameters, the universal reference is deduced as const lvalue reference
f(to_string(1234)); // to_string The function returns one string Temporary objects , It's right value , After the parameter is passed, the universal reference is deduced as the right value reference
const double d = 1.1;
f(std::move(d)); // const The lvalue is move Later into const Right value , After passing the parameters, the universal reference is deduced as const Right quoting
return 0;
}
Open the monitoring window under debugging to see the parameters after parameter transmission t The type of :
So we will use universal quotation to do something meaningful , Consider the following code :
void Func(int& x) {
cout << " lvalue reference " << endl; }
void Func(const int& x) {
cout << "const lvalue reference " << endl; }
void Func(int&& x) {
cout << " Right quoting " << endl; }
void Func(const int&& x) {
cout << "const Right quoting " << endl; }
template<typename T>
void f(T&& t) // Omnipotent quotation
{
Func(t); // According to the parameters t Type to match the appropriate overloaded function
}
int main()
{
int a = 4; // The left value
f(a);
const int b = 8; // const The left value
f(b);
f(10); // 10 It's right value
const int c = 13;
f(std::move(c)); // const The lvalue is move Later into const Right value
return 0;
}
After running the program , We thought the result of printing was :
lvalue reference
const lvalue reference
Right quoting
const Right quoting
But the actual result is :
The results of the last two lines are different from what we expected .
So what's the matter ?
In fact, I have talked about it before in this article , An R-value reference variable is actually an l-value , So we have the above running results .
Specific explanation :
f(10);
10 It's right value , After the parameter is passed, the universal reference is deduced as the right value reference , But the R-value reference variable is actually an l-value , So the function actually called isvoid Func(int& x).f(std::move(c));
const The lvalue is move Later into const Right value , After passing the parameters, the universal reference is deduced as const Right quoting , But it's time to const The right value reference variable is actually const The left value , So the function actually called isvoid Func(const int& x).
in other words , The right value reference has lost the right value attribute .
But what we want is , In the process of transmission, it can maintain its original l-value or R-value attribute , therefore C++11 The standard proposes perfect forwarding .
(2) Concept
Perfect forwarding is in function templates , Exactly according to the parameter type of the template , Pass the parameter to another function in the current function template .
therefore , In order to achieve perfect forwarding , In addition to using universal references , We need to use std::forward(C++11), it Retain the native type attribute of the object during parameter passing .
In this way, the right value reference can maintain the right value attribute in the transmission process .
void Func(int& x) {
cout << " lvalue reference " << endl; }
void Func(const int& x) {
cout << "const lvalue reference " << endl; }
void Func(int&& x) {
cout << " Right quoting " << endl; }
void Func(const int&& x) {
cout << "const Right quoting " << endl; }
template<typename T>
void PerfectForward(T&& t) // Omnipotent quotation
{
Func(std::forward<T>(t)); // According to the parameters t Type to match the appropriate overloaded function
}
int main()
{
int a = 4; // The left value
PerfectForward(a);
const int b = 8; // const The left value
PerfectForward(b);
PerfectForward(10); // 10 It's right value
const int c = 13;
PerfectForward(std::move(c)); // const The lvalue is move Later into const Right value
return 0;
}
The operation results are as follows :
To achieve perfect forwarding requires universal reference and std::forward .
(3) Use scenarios
In addition to the above scenarios ,C++11 The standard STL The relevant interface functions of the container also realize perfect forwarding , In this way, we can really realize the value of right value reference .
such as STL Containers in the Library list :
The above four interface functions call _Insert function ,_Insert Function template realizes perfect forwarding .
Another example is the self Simulated Implementation list( Only the main part is written here ):
template<class T>
struct ListNode
{
ListNode* _next = nullptr;
ListNode* _prev = nullptr;
T _data;
};
template<class T>
class List
{
typedef ListNode<T> Node;
public:
List()
{
_head = new Node;
_head->_next = _head;
_head->_prev = _head;
}
void PushBack(const T& x) // lvalue reference
{
Insert(_head, x);
}
void PushFront(const T& x) // lvalue reference
{
Insert(_head->_next, x);
}
void PushBack(T&& x) // Right quoting
{
Insert(_head, std::forward<T>(x)); // Key points : Keep the native type properties of the object
}
void PushFront(T&& x) // Right quoting
{
Insert(_head->_next, std::forward<T>(x)); // Key points : Keep the native type properties of the object
}
template<class TPL> // This function template realizes perfect forwarding
void Insert(Node* pos, TPL&& x) // Omnipotent quotation
{
Node* prev = pos->_prev;
Node* newnode = new Node;
newnode->_data = std::forward<TPL>(x); // Key points : Keep the native type properties of the object
// prev newnode pos
prev->_next = newnode;
newnode->_prev = prev;
newnode->_next = pos;
pos->_prev = newnode;
}
private:
Node* _head;
};
As long as it is an R-value reference , Pass the current function to other function calls , Keep the right value attribute , Must achieve perfect forwarding .
4. Great significance
Right quoting ( It supports mobile semantics and perfect forwarding ) yes C++11 One of the most important new features added to , It makes the C++ The program runs more efficiently .
边栏推荐
- Ribbon load balancing strategy and configuration, lazy loading and hungry loading of ribbon
- 深度学习领域图像分割FCN(Fully Convolutional Networks for Semantic Segmentation)
- Elastic certification test: 30 day FastPass Study Guide
- 电商分账系统重要吗,平台应该如何选择分账服务商呢?
- Hash table questions (Part 2)
- ELS compatibility DC, transfer pictures to window
- e. The difference between target and e.currenttarget
- Using webmvcconfigurer to intercept interface requests is being enhanced (with source code)
- Brightcove appoints Dan Freund as chief revenue Officer
- 好用移动APP自动化测试框架哪里找?收藏这份清单就好了!
猜你喜欢

IIC 通信协议 (一)

Brightcove任命Dan Freund为首席营收官

BSN IPFs (interstellar file system) private network introduction, functions, architecture and characteristics, access instructions

BSN IPFS(星际文件系统)专网简介、功能、架构及特性、接入说明

e.target与e.currentTarget的区别
![Shell的正则表达式入门、常规匹配、特殊字符:^、$、.、*、字符区间(中括号):[ ]、特殊字符:\、匹配手机号](/img/31/ed0d8c1a5327059f2de7493bec1c6c.png)
Shell的正则表达式入门、常规匹配、特殊字符:^、$、.、*、字符区间(中括号):[ ]、特殊字符:\、匹配手机号

管理信息系统期末复习

Echart柱状图中数据显示在图上方

Brightcove appoints Dan Freund as chief revenue Officer

2022-07-26:以下go语言代码输出什么?A:5;B:hello;C:编译错误;D:运行错误。 package main import ( “fmt“ ) type integer in
随机推荐
Elastic certification test: 30 day FastPass Study Guide
grid布局
ros 相机标定 sensor_msgs/CameraInfo Message 数据类型及含义
Spark practice case (upgraded version)
F - Pre-order and In-order(Atcoder 255)
Use the kubesphere graphical interface dashboard to enable the Devops function
Easy to use shell shortcuts
Hash (hash)
Convolution neural network -- a detailed introduction to convolution of 24 bit color images
The external symbol parsed by the method "public: virtual _ucdecl nvinfer1:: yololayerplugin:: ~yololayerplugin (void)" "public: virtual
【软件工程期末复习】知识点+大题详解(E-R图、数据流图、N-S盒图、状态图、活动图、用例图....)
深度剖析 —— 动态内存管理
C get UUID
Final review of management information system
els_ Rectangle drawing, code planning and backup
Standard C language 11
e.target与e.currentTarget的区别
结构型模式-桥接模式
Using webmvcconfigurer to intercept interface requests is being enhanced (with source code)
使用WebMvcConfigurer进行接口请求拦截进行中增强(附源码)

















