C++左值右值/左右值引用/std::move()
C++左值右值/左右值引用/std::move()
左值/右值
- 左值(lvalue)是放在赋值语句左边可以被赋值的值(不是变量!),左值必须在内存中有一个确定的地址。
- 右值(rvalue)用来放在赋值语句右边,将自己的值取出赋给别的变量,右值没有一个确定的地址。
也可以这么理解:
左值是指表达式结束后依然存在的持久化对象
右值是指表达式结束时就不再存在的临时对象。所有的具名变量或者对象都是左值,而右值不具名(所以匿名变量是一个右值)。
《C++ Primer(第5版)》中描述左值和右值:
- 当对象被用作左值的时候,用的是对象的身份(在内存中的位置)
- 当一个对象被用作右值的时候,用的是对象的值(内容)
以上三个说法都是非常容易理解的,下面我们举个例子:
1 |
|
int x
中的x
是一个左值,因为他是赋值语句=
左边的值,表达式结束后依然存在的持久化对象,用的是对象的身份(在内存中的位置,向内存写入0)&x
是返回一个右值,它指向了对象x的地址,通过赋值运算符=
,将对象x
的地址(右值)赋值给了一个新定义的左值对象px
。
常见的右值:“abc”,123这种字面值常量和表达式求值过程中创建的临时对象,还有typename()
这种匿名变量/匿名对象。
访问关系上来看
一般来说:右值可以读,不可以写;左值既可以读,也可以写。
当然有一些例外情况:
即使它是左值,也不可以被修改,比如
const
限定符,const int a = 3
,a
是不可以被修改的。右值在某些情况下也可以写,就是右值引用
- ```c++
int a = 0;
int &&temp = a + 3;1
2
3
4
5
6
7
8
9
10
11
12
13
### 从替代关系上来看
**一般来说:**需要右值的地方,都可以使用左值来替代。
当然有一些**例外情况**:
- 右值引用接收右值,拒绝左值
- ```c++
int a = 0;
int &&temp = a + 3;//yes 右值引用接受 a + 3这个表达式(右值)
int &&temp1 = a; // no 右值引用不接受a这个左值
- ```c++
左值引用/右值引用
基本介绍
引用是变量的别名,必初始化,C++引入的,C语言只有指针,没有引用。引用操作从反汇编层面看可以说完全指针一样,从使用层面来说的确降低大家都指针的理解成本,传参时引用本意减少拷贝,提高性能,但由于是编译器的内部转为为指针,有时比指针的灵活性弱一点点,像原来拷贝构造函数const T&
左引用存在一定的缺陷,右值引用带来的移动语义就是来弥补。
指向左值的引用就是左引用,我们单个&
来表示,C++11前一直使用的;对右值的引用是右引用,我们用&&
来表示,如下面代码示例:
1 |
|
注意:虽然右值引用只能引用右值,但是右值引用本身却是左值。
1 |
|
因此rr_1
单独使用,是一个表达式——一个没有运算符的表达式,它返回的是左值;因此不可以将右值引用绑定到左值rr_1
上。
一种特殊的常量左值引用
有没有直接对右值进行左引用的?
还真有,那就是const T&
,常量左值引用,能接受右值,对右值进行这种形式左引用写法也不少,其生命周期被延续。
这个const T&
最大的好处就是:
工业界实践的时候,如果C++中,函数传参,不改变参数时,尤其是大数据,尽量使用const T&
。我们常用的拷贝构造函数T(const T&)参数是这个形式,vector容器的函数push_back(const value_type& val)参数也是,有没有注意到,这类函数同时接受右值和左值。
左右值转换:std::move
既然是左/右值引用数据类型,就存在转换关系。
我们可以使用标准库函数std::move
得到左值的右值引用类型。
1 |
|
move
调用告诉编译器,我们有一个左值rr_1
,但是我希望像一个右值一样处理它。我们必须保证,接下来除了对rr_1
赋值或销毁它,我们不再使用它。我们不能对rr_1
的值作任何假设。
《C++ Primer(第5版)》推荐使用std::move
而不是move
,可以避免潜在的命名冲突。
std::move的定义:
这里,T&&
是通用引用,需要注意和右值引用(比如int&&
)区分。通过move
定义可以看出,move并没有”移动“什么内容,只是将传入的值转换为右值,此外没有其他动作。std::move
+移动构造函数或者移动赋值运算符 ,也就是两者合力才起到这样的作用,才能充分起到减少不必要拷贝的意义。
std::move使用前提:
定义的类使用了资源并定义了移动构造函数和移动赋值运算符 (右值和移动构造函数/移动赋值函数配合使用才能起到资源直接转移的作用,减少不必要的拷贝)
该变量即将不再使用 (很容易理解:move后就会变成右值,右值不再具有资源,而单纯作为一个值,不再作为一个对象)
关于move到底是怎么做到左右值转换的请看这篇文章:
https://blog.csdn.net/daaikuaichuan/article/details/88371948
简单总结下来就是:我们通过static_cast<>
进行强制类型转换返回T&&
右值引用,而static_cast
之所以能使用类型转换,是通过remove_refrence::type
的偏特化模板移除T&&,T&的引用,获取具体类型T(模板偏特化)。
本博客所有文章除特别声明外,均采用 CC BY-SA 4.0 协议 ,转载请注明出处!