左值,右值,将亡值,左值引用,右值引用的归纳与辨析

二木 王者

左值,右值,将亡值,左值引用,右值引用的归纳与辨析

Owner: 二木

什么是左值,右值,将亡值?

这个见字知意,简单的来说所谓左值,就是处于等号左边的值,右值就是等号右边的值。

比如

1
int a = 10;

a就是左值,10就是右值,这个是简单的理解

用更准确的来说

左值(lvalue) :表达式结束后依然存在的持久对象

右值(rvalue) :表达式结束后就不再存在的临时对象

左值的英文简写为“lvalue”,右值的英文简写为“rvalue”。很多人认为它们分别是”left value”、”right value” 的缩写,其实不然。lvalue 是“loactor value”的缩写,可意为存储在内存中、有明确存储地址(可寻址)的数据,而 rvalue 译为 “read value”,指的是那些可以提供数据值的数据(不一定可以寻址,例如存储于寄存器中的数据)

将亡值 : 所谓将亡值,将亡。就是将要消失的值,有个很典型,也是将亡经常出现的地方的例子,就是移动构造函数

什么是左值引用,右值引用?

对于引用在学习c++基础应该还是比较了解的

所谓引用就是给一个存在的对象定义的别名,一个变量可以有多个引用,引用必须初始化,引用只能在初始化的时候引用一次,不能更改引用其他变量。这就是一个一对多的映射,相当于一个地址存在对应存在着多个映射,在不同的别名下,都能访问到这个位置。

1
2
int num = 10;
int &a = num;

这就相当于对num取了一个别名叫a.这个引用也叫左值引用

对于右值引用的引入

引用使用“&”符号,但是此种引用有一个缺陷,即正常情况下只能操作 C++ 中的左值,无法对右值添加引用。

1
2
3
int num = 10;
int &b = num; //正确
int &c = 10; //不行

编译器允许我们为 num 左值建立一个引用,但不可以为 10 这个右值建立引用。

“&”表示的引用又称为左值引用。

常量左值引用既可以操作左值,也可以操作右值。

1
2
3
int num = 10;
const int &b = num;
const int &c = 10;

右值往往是没有名称的,因此要使用它只能借助引用的方式。这就产生一个问题,实际开发中我们可能需要对右值进行修改(主要是实现移动语义时就需要),显然左值引用的方式是行不通的。

右值引用,用 “&&” 表示。

右值引用的使用

和声明左值引用一样,右值引用也必须立即进行初始化操作,且只能使用右值进行绑定

1
2
3
int num = 10;
//int && a = num; //右值引用不能初始化为左值
int && a = 10;

右值引用可以修改右值

1
2
3
int && a = 10;
a = 11;
cout << a << endl; //输出结果为11
1
const int&& a = 10;//编译器不会报错

但这种形式的右值引用并没有实际用处。右值引用主要用于**移动语义和完美转发**,其中前者需要有修改右值的权限

什么是将亡值?

将亡值是右值的一种特殊类型,表示该右值即将被销毁或移动。通过使用将亡值,我们可以利用移动语义来避免不必要的复制操作,提高代码的效率。

将亡值的一下使用:

将亡值有三种常用的情况,分别为移动语义,完美转发,右值引用。

移动语义:

1
2
3
4
5
6
7
std::string createString() {
std::string str = "Hello";
return str; // 返回将亡值
}

std::string str = createString(); // 将亡值被移动到 str 中

像这里,我们声明的std::string str = “Hello”;由于在函数内部,当退出函数的时候,std::string str = “Hello”;这个字符串就会被销毁,然而createString() 函数返回将亡值(str),然后将亡值被移动到 str 变量中,避免了不必要的字符串复制操作。就使得函数内部str的”Hello”;的所有权,转移到了函数外std::string str = createString(); 这个str上,实现了所有权的转移。

完美转发:

1
2
3
4
5
6
7
8
template<typename T>
void forwardValue(T&& value) {
process(std::forward<T>(value)); // 完美转发将亡值
}

std::string str = "Hello";
forwardValue(std::move(str)); // 将亡值被完美转发

在这个例子中,forwardValue() 函数使用完美转发来接收参数,并将参数完美转发给 process() 函数。通过使用 std::forward,将亡值的值类别被保留,确保参数以原始的值类别传递。

右值引用:

1
2
3
4
5
6
7
std::vector<int> createVector() {
std::vector<int> vec = {1, 2, 3};
return vec; // 返回将亡值
}

std::vector<int> && rvalueRef = createVector(); // 将亡值绑定到右值引用

在这个例子中,createVector() 函数返回一个将亡值(vec),然后将亡值通过右值引用 && 绑定到 rvalueRef 变量上。

像上面几种,对将亡值的使用,使得我们的代码,并没有对数据进行了大量的复制,就将我们的数据从一个变量转移到另一个变量上,对于处理大的数据的时候,这样格外节省内存资源与效率。

C++高阶知识:深入分析移动构造函数及其原理 | 音视跳动科技 (avdancedu.com)

【C++】左值和右值、左值引用(&)和右值引用(&&)_c++ arg&&_Jacky_Feng的博客-CSDN博客

 评论