自学内容网 自学内容网

C++——左值和右值的本质区别

左值和右值好干嘛?

深入理解左值和右值可以帮助我们对代码进行优化

一、什么是左值和右值

左值:有某种存储支持的变量
右值:临时值(字面量、函数的结果)

Ⅰ右值是字面量

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修饰?

看了很多博客,解释各有差异,也有很多坑
这是我理解透之后总结出来的,感谢网上的很多博主的博客,看了很多,这里就不一一列举了

如果该篇博文对您产生了一丢丢的帮助,还请各位观众老爷给个赞,谢谢~~


原文地址:https://blog.csdn.net/qq_41264055/article/details/143664595

免责声明:本站文章内容转载自网络资源,如本站内容侵犯了原著者的合法权益,可联系本站删除。更多内容请关注自学内容网(zxcms.com)!