程序员之家
程序员之家

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

2024-11-11 16:45:06 431
左值和右值到底是什么意思 ?深入理解左值和右值可以帮助我们对代码进行优化。

什么是左值和右值

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

1. 右值是字面量

int yy = 22;

22本身就是一个临时的,系统不会存储它,故22是右值
yy是一个int型变量,系统会为其开辟一个int大小的内存空间,故yy是左值

这下就应该很容易理解了吧

右值可以赋值给左值,左值不可以赋值给右值

int yy = 99;//右值99可以赋值给左值yy
10 = yy;//左值yy不可以赋值给右值10

其本质是因为左值是有地址的,有存储空间;而右值没有,是一个临时值而已,存活时间不长

这时候就可以优化代码了,我们不需要过于关注右值,因为它活不长,这就涉及到优化的问题了

可以证明字面量是个右值!

2. 右值是函数的结果

①函数无参数
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;
}

左值引用

1. 函数返回类型为左值引用

函数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;
}

2. 函数参数为左值引用

函数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&可以接收左值和右值,建议使用

3. 如何判断是左值还是右值

写个函数,函数的参数是左值引用即可,因为右值的话传不进来
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修饰?


手机扫码阅读本文


本文来自互联网,本网站转载的目的在于传递更多信息以供访问者学习参考,所属内容只代表原作者的个人观点,不代表本网站的立场和价值判断,版权归原作者所有。如有侵犯您的版权,请联系我们,我们收到后会尽快核实并第一时间改正。


手机扫码阅读本文

C++标准库【图】
C++标准库

畅销全球 阔别12载携C++11新标准重磅归来 全面覆盖新标准新成员新特性 全书例子完全基于C++11重写

Python 从入门到精通【图】
Python 从入门到精通

Python入门经典,26万Python程序员的入行选择。配备升级版Python开发资源库,在线大咖课+在线答疑,学习1小时,训练10小时

Go语言设计与实现(全彩印刷,图解Go底层原理,深度剖析Go源码)【图】
Go语言设计与实现(全彩印刷,图解Go底层原理,深度剖析Go源码)

近200幅精美全彩配图生动图解Go底层原理,带你轻松读懂Go源码,完成Go进阶,"面向信仰编程"博主@Draven作品

B202411116434