左值,右值,将亡值,左值引用,右值引用的归纳与辨析
左值,右值,将亡值,左值引用,右值引用的归纳与辨析
Owner: 二木
什么是左值,右值,将亡值?
这个见字知意,简单的来说所谓左值,就是处于等号左边的值,右值就是等号右边的值。
比如
1 | int a = 10; |
a就是左值,10就是右值,这个是简单的理解
用更准确的来说
左值(lvalue) :表达式结束后依然存在的持久对象。
右值(rvalue) :表达式结束后就不再存在的临时对象。
左值的英文简写为“lvalue”,右值的英文简写为“rvalue”。很多人认为它们分别是”left value”、”right value” 的缩写,其实不然。lvalue 是“loactor value”的缩写,可意为存储在内存
中、有明确存储地址
(可寻址)的数据,而 rvalue 译为 “read value”,指的是那些可以提供数据值的数据
(不一定可以寻址,例如存储于寄存器中的数据)
将亡值 : 所谓将亡值,将亡。就是将要消失的值,有个很典型,也是将亡经常出现的地方的例子,就是移动构造函数
什么是左值引用,右值引用?
对于引用在学习c++基础应该还是比较了解的
所谓引用就是给一个存在的对象定义的别名,一个变量可以有多个引用,引用必须初始化,引用只能在初始化的时候引用一次,不能更改引用其他变量。这就是一个一对多的映射,相当于一个地址存在对应存在着多个映射,在不同的别名下,都能访问到这个位置。
1 | int num = 10; |
这就相当于对num取了一个别名叫a.这个引用也叫左值引用
对于右值引用的引入
引用使用“&”符号,但是此种引用有一个缺陷,即正常情况下只能操作 C++ 中的左值,无法对右值添加引用。
1 | int num = 10; |
编译器允许我们为 num 左值建立一个引用,但不可以为 10 这个右值建立引用。
“&”表示的引用又称为左值引用。
常量左值引用既可以操作左值,也可以操作右值。
1 | int num = 10; |
右值往往是没有名称的,因此要使用它只能借助引用的方式。这就产生一个问题,实际开发中我们可能需要对右值进行修改(主要是实现移动语义时就需要),显然左值引用的方式是行不通的。
右值引用,用 “&&” 表示。
右值引用的使用
和声明左值引用一样,右值引用也必须立即进行初始化操作,且只能使用右值进行绑定
1 | int num = 10; |
右值引用可以修改右值
1 | int && a = 10; |
1 | const int&& a = 10;//编译器不会报错 |
但这种形式的右值引用并没有实际用处。右值引用主要用于**移动语义和完美转发
**,其中前者需要有修改右值的权限
什么是将亡值?
将亡值是右值的一种特殊类型,表示该右值即将被销毁或移动。通过使用将亡值,我们可以利用移动语义来避免不必要的复制操作,提高代码的效率。
将亡值的一下使用:
将亡值有三种常用的情况,分别为移动语义,完美转发,右值引用。
移动语义:
1 | std::string createString() { |
像这里,我们声明的std::string str = “Hello”;由于在函数内部,当退出函数的时候,std::string str = “Hello”;这个字符串就会被销毁,然而createString()
函数返回将亡值(str
),然后将亡值被移动到 str
变量中,避免了不必要的字符串复制操作。就使得函数内部str的”Hello”;的所有权,转移到了函数外std::string str = createString(); 这个str上,实现了所有权的转移。
完美转发:
1 | template<typename T> |
在这个例子中,forwardValue()
函数使用完美转发来接收参数,并将参数完美转发给 process()
函数。通过使用 std::forward
,将亡值的值类别被保留,确保参数以原始的值类别传递。
右值引用:
1 | std::vector<int> createVector() { |
在这个例子中,createVector()
函数返回一个将亡值(vec
),然后将亡值通过右值引用 &&
绑定到 rvalueRef
变量上。
像上面几种,对将亡值的使用,使得我们的代码,并没有对数据进行了大量的复制,就将我们的数据从一个变量转移到另一个变量上,对于处理大的数据的时候,这样格外节省内存资源与效率。