Hello 大家好,今天我们来学习左值和右值
1 左值和右值
• 左值是一个表示数据的表达式(如变量名或解引用的指针),一般是有持久状态,存储在内存中,我们可以获取它的地址,左值可以出现赋值符号的左边,也可以出现在赋值符号右边。定义时const修饰符后的左值,不能给他赋值,但是可以取它的地址。
• 右值也是一个表示数据的表达式,要么是字面值常量、要么是表达式求值过程中创建的临时对象等,右值可以出现在赋值符号的右边,但是不能出现出现在赋值符号的左边,右值不能取地址。
• 值得一提的是,左值的英文简写为lvalue,右值的英文简写为rvalue。传统认为它们分别是leftvalue、right value 的缩写。现代C++中,lvalue 被解释为loactor value的缩写,可意为存储在内存中、有明确存储地址可以取地址的对象,而 rvalue 被解释为 read value,指的是那些可以提供数据值,但是不可以寻址,例如:临时变量,字面量常量,存储于寄存器中的变量等,也就是说左值和右值的核心区别就是能否取地址。
#include<iostream>
using namespace std;
int main()
{
// 左值:可以取地址
// 以下的p、b、c、*p、s、s[0]就是常见的左值
int* p = new int(0);
int b = 1;
const int c = b;
*p = 10;
string s("111111");
s[0] = 'x';
cout << &c << endl;
cout << (void*)&s[0] << endl;
// 右值:不能取地址
double x = 1.1, y = 2.2;
// 以下几个10、x + y、fmin(x, y)、string("11111")都是常见的右值
//字面量常量
10;
//表达式求值过程中创建的临时对象
x + y;
//存储于寄存器中的变量:函数返回值
fmin(x, y);
//匿名对象是临时对象
string("11111");
//右值均不能取地址,会报错
/*cout << &10 << endl;
cout << &(x+y) << endl;
cout << &(fmin(x, y)) << endl;
cout << &string("11111") << endl;*/
return 0;
}
2 左值引用和右值引用
• Type& r1 = x; Type&& rr1 = y; 第一个语句就是左值引用,左值引用就是给左值取别名,第二个就是右值引用,同样的道理,右值引用就是给右值取别名。
• 左值引用不能直接引用右值,但是const左值引用可以引用右值
• 右值引用不能直接引用左值,但是右值引用可以引用move(左值)
• move是库里面的一个函数模板,本质内部是进行强制类型转换
• 需要注意的是变量表达式都是左值属性,也就意味着一个右值被右值引用绑定后,右值引用变量变量表达式的属性是左值
• 语法层面看,左值引用和右值引用都是取别名,不开空间。从汇编底层的角度看下面代码中r1和rr1汇编层实现,底层都是用指针实现的,没什么区别。底层汇编等实现和上层语法表达的意义有时是背离的,所以不要然到一起去理解,互相佐证,这样反而是陷入迷途。
#include<iostream>
//move库文件
#include<utility>
using namespace std;
int main()
{
// 左值:可以取地址
// 以下的p、b、c、*p、s、s[0]就是常见的左值
int* p = new int(0);
int b = 1;
const int c = b;
*p = 10;
string s("111111");
s[0] = 'x';
double x = 1.1, y = 2.2;
// 左值引用给左值取别名
int& r1 = b;
int*& r2 = p;
int& r3 = *p;
string& r4 = s;
char& r5 = s[0];
// 右值引用给右值取别名
int&& rr1 = 10;
double&& rr2 = x + y;
double&& rr3 = fmin(x, y);
string&& rr4 = string("11111");
// 左值引用不能直接引用右值,但是const左值引用可以引用右值
const int& rx1 = 10;
const double& rx2 = x + y;
const double& rx3 = fmin(x, y);
const string& rx4 = string("11111");
// 右值引用不能直接引用左值,但是右值引用可以引用move(左值)
int&& rrx1 = move(b);
int*&& rrx2 = move(p);
int&& rrx3 = move(*p);
//move本质是进行强制类型转换,rrx4和rrx5效果一样
string&& rrx4 = move(s);
string&& rrx5 = (string&&)s;
// b、r1、rr1都是变量表达式,都是左值
cout << &b << endl;
cout << &r1 << endl;
//变量表达式都是左值属性,也就意味着一个右值被右值引用绑定后,右值引用变量变量表达式的属性是左值
cout << &rr1 << endl;
// 这里要注意的是,rr1的属性是左值,所以不能再被右值引用绑定,除非move一下
int& r6 = r1;
// int&& rrx6 = rr1;
int&& rrx6 = move(rr1);
return 0;
}
3 引用延长生命周期
右值引用可用于为临时对象延长生命周期,const 的左值引用也能延长临时对象生存期,但这些对象无法被修改。
#include<iostream>
using namespace std;
class result
{
public:
result()
{
cout << "构造:result()"<<'\n';
}
~result()
{
cout << "析构:~result()"<<'\n';
}
};
int main()
{
std::string s1 = "Test";
// std::string&& r1 = s1; // 错误:不能绑定到左值
//临时对象的生命周期只有本行
const std::string& s2 = s1 + s1; // OK:到 const 的左值引用延长生存期
//引用后生命周期延长到r2的生命周期
// r2 += "Test"; // 错误:不能通过到 const 的引用修改
std::string&& s3 = s1 + s1; // OK:右值引用延长生存期
s3 += "Test"; // OK:能通过到非 const 的引用修改
std::cout << s3 << '\n';
//我们来实践看一下
//result()创建了一个临时对象
result();
//result()创建了一个临时对象,它是一个右值
// 通过结果我们可以看到直到main函数结束时这个对象才调用了析构函数
result&& r1 = result();
//result()创建了一个临时对象,它是一个const左值引用
const result& r2 = result();
return 0;
}
result()创建了一个临时对象,result();语句执行完成后,临时对象的生命周期结束,调用析构函数
右值引用和const左值引用时,直到main函数结束时这个对象才调用了析构函数
4 左值和右值的参数匹配
• C++98中,我们实现一个const左值引用作为参数的函数,那么实参传递左值和右值都可以匹配。
• C++11以后,分别重载左值引用、const左值引用、右值引用作为形参的f函数,那么实参是左值会匹配f(左值引用),实参是const左值会匹配f(const 左值引用),实参是右值会匹配f(右值引用)。
• 右值引用变量在用于表达式时属性是左值,这个设计这里会感觉跟怪,下一小节我们讲右值引用的使用场景时,就能体会这样设计的价值了
#include<iostream>
using namespace std;
void f(int& x)
{
std::cout << "左值引用重载 f(" << x << ")\n";
}
void f(const int& x)
{
std::cout << "到 const 的左值引用重载 f(" << x << ")\n";
}
void f(int&& x)
{
std::cout << "右值引用重载 f(" << x << ")\n";
}
int main()
{
int i = 1;
const int ci = 2;
f(i); // 调用 f(int&)
f(ci); // 调用 f(const int&)
f(3); // 调用 f(int&&),如果没有 f(int&&) 重载则会调用 f(const int&)
f(std::move(i)); // 调用 f(int&&)
// 右值引用变量在用于表达式时是左值
int&& x = 1;
f(x); // 调用 f(int& x)
f(std::move(x)); // 调用 f(int&& x)
return 0;
}
到此,左值后右值就讲完了,怎么样,是不是感觉大脑里面多了很多新知识。
如果觉得博主讲的还可以的话,就请大家多多支持博主,收藏加关注,追更不迷路
如果觉得博主哪里讲的不到位或是有疏漏,还请大家多多指出,博主一定会加以改正
博语小屋将持续为您推出文章