左值和右值好干嘛?
深入理解左值和右值可以帮助我们对代码进行优化
一、什么是左值和右值
左值:有某种存储支持的变量
右值:临时值(字面量、函数的结果)
Ⅰ右值是字面量
int yy = 22;
22本身就是一个临时的,系统不会存储它,故22是右值
而yy是一个int型变量,系统会为其开辟一个int大小的内存空间,故yy是左值
这下就应该很容易理解了吧
右值可以赋值给左值,左值不可以赋值给右值
int yy = 99;//右值99可以赋值给左值yy
10 = yy;//左值yy不可以赋值给右值10
其本质是因为左值是有地址的,有存储空间;而右值没有,是一个临时值而已,存活时间不长
这时候就可以优化代码了,我们不需要过于关注右值,因为它活不长,这就涉及到优化的问题了
可以证明字面量是个右值!
Ⅱ右值是函数的结果
①函数无参数
beyond是一个返回一个int的函数,其是一个右值
int year = beyond();
year是左值,函数的结果是个右值,右值可以赋值给左值,正确
beyond() = year;
报错,因为beyond()是右值,而year是左值,左值不可以赋值给右值
int beyond()
{
return 1993;
}
int main()
{
int year = beyond();
beyond() = year;//error,表达式必须是可修改的左值
return 0;
}
可以证明函数的结果是右值!
②函数有参数
函数beyond接收的参数为左值,因为year是一个int型,有存储地址
beyond(1993);
若调用函数beyond,传入右值也是可以的
这种情况,当函数被调用时,会自动通过该右值1993来创建一个左值,例如:int temp = 1993; beyond(temp);
int beyond(int year)
{
return year;
}
int main()
{
int year =1993;
beyond(year);
beyond(1993);//1993为临时变量右值,它会被系统自动转为一个左值使用
return 0;
}
二、左值引用
Ⅰ函数返回类型为左值引用
函数beyond返回值为左值引用(int&),也就是return year;
这个左值的引用
这下子,函数的结果就变成了左值,其值就是左值year
int year = beyond();
左值引用beyond可以赋值给左值
beyond() = 1999;
因为beyond()时左值,故可以将右值1999赋值给左值beyond()
#include <iostream>
int& beyond()
{
int year = 1993;
std::cout << "beyond_year: " << year << std::endl;
return year;
}
int main()
{
int year = beyond();//√
beyond() = 1999;//√
return 0;
}
Ⅱ函数参数为左值引用
函数beyond的参数为左值引用(int& year),int year是个左值,取它的引用
void beyond(int& year){}
int main()
{
int year = 1993;
beyond(year);//√
beyond(1993);//× 非常量引用的初始值必须为左值
return 0;
}
可以看到,函数参数为左值引用,若传入一个右值会报错!
变通方法:加上const即可
这也是很多项目里面函数参数常使用const的原因之一,因为函数参数可以传入左值和右值
const int& year
加上const就可以了,既可以传入左值,也可以传入右值
void beyond(const int& year){}
int main()
{
int year = 1993;
beyond(year);//√
beyond(1993);//√
return 0;
}
const+左值引用的本质
右值10是不可以赋值给左值引用int &a的
int& a = 10;//×
若加上const就可以了,因为有编译器默默的进行了转换,将右值10转换为一个临时变量temp,然后把这个临时变量赋值给左值
const int& a = 10;//√
//等价于
int temp = 10;
int &a = temp;
const int&
可以接收左值和右值,建议使用
Ⅲ如何判断是左值还是右值
写个函数,函数的参数是左值引用即可,因为右值的话传不进来
vocal、guitarist 、band
是左值
"Huangjiaju"、"Huangguanzhong"和vocal + guitarist
是右值
其中vocal + guitarist
组成了一个临时字符串,然后赋值给左值band
临时值也就是右值,没有地址
#include <iostream>
void beyond(std::string& name)
{
std::cout << name << std::endl;
}
int main()
{
std::string vocal = "Huangjiaju";
std::string guitarist = "Huangguanzhong";
std::string band = vocal + guitarist;
beyond(vocal);
beyond(guitarist);
beyond(band);
beyond(vocal + guitarist);//×
return 0;
}
常量引用(如const int&)能兼容临时的右值和实际存在的左值变量
三、右值引用
左值引用(例如:const int&
)接收左值,类似的,右值引用接收右值(临时对象)
右值引用,例如:const int&&
,相对于左值引用多了一个&
将函数beyond的参数改为右值引用std::string&& name
,报错的就是传入左值了
#include <iostream>
void beyond(std::string&& name)
{
std::cout << name << std::endl;
}
int main()
{
std::string vocal = "Huangjiaju";
std::string guitarist = "Huangguanzhong";
std::string band = vocal + guitarist;
beyond(vocal);//×
beyond(guitarist);//×
beyond(band);//×
beyond(vocal + guitarist);//√
return 0;
}
此时,肯定会有同学发现,若要进行函数重载,一个是const+左值引用
;另一个是右值引用
会咋样?
const + 左值引用
支持左值和右值(const std::string& name
)
右值引用只能传入右值(std::string&& name
)
#include <iostream>
void beyond(const std::string& name)
{
std::cout << "const + 左值引用" << name << std::endl;
}
void beyond(std::string&& name)
{
std::cout << "右值引用" << name << std::endl;
}
int main()
{
std::string vocal = "Huangjiaju";
std::string guitarist = "Huangguanzhong";
std::string band = vocal + guitarist;
beyond(vocal);//√
beyond(guitarist);//√
beyond(band);//√
beyond(vocal + guitarist);//√
return 0;
}
贴下结果
分析:可以看到beyond(vocal + guitarist);
调用的是右值引用的函数
vocal + guitarist
是右值,std::string&& name
只能接收右值,const std::string& name
既可以接收左值,也可以接收右值
编译器最终选择了std::string&& name
这种设计的主要目的是允许开发者提供特定于右值的优化,如移动语义,从而提高代码的性能
四、小结
左值是有实际内存空间的,右值是临时值
左值引用只接受左值,右值引用只接受右值
左值引用加上const既可以接收左值也可以接收右值
会不会有同学问:为啥没有右值引用加const呢?
是不是小傻蛋?右值就是一个临时值,很快就会消失的,哪来的const修饰?
看了很多博客,解释各有差异,也有很多坑
这是我理解透之后总结出来的,感谢网上的很多博主的博客,看了很多,这里就不一一列举了
如果该篇博文对您产生了一丢丢的帮助,还请各位观众老爷给个赞,谢谢~~