需要时刻牢记的是:函数的形参都是左值,即使它的类型是右值引用,它也是左值,因为它可以被取地址。
主要是区分什么是右值,满足以下二者之一即可:
- 临时对象
- 无法位于赋值符号右边的对象
std::move
和std::forward
std::move
相当于无条件的将其参数转换为右值,而std::forward
则是判定条件否则满足来进行转换。
这就是说,这两个函数都是做的
cast
动作,并没有做其他的操作。
std::move
的简易实现如下:
1 | //c++11 版本 |
可以看到,move
函数将参数param
使用static_cast
强制转换为了remove_reference<T>::type&&
,也就是参数的右值引用。
也就是说标准的std::move
也仅仅是做转换作用,它并没有像它的名字那样实现移动的操作,而是向编译器指明,该对象是可以被移动的。
有了这个基础,就可以理解下面的代码:
1 |
|
理解std::move
对于std::string
,其有构造函数如下:
1 | class string { // std::string is actually a |
一个是接收const string
的左值引用的拷贝构造函数,另一个是string
的右值引用移动构造函数,因为移动构造函数是要修改形参对象内存的,所以不能添加const
限定。
下面假设有一个类的构造函数需要传入string
:
1 | class Annotation { |
这个类的构造函数接受一个const std::string
来完成对私有成员value
的赋值。
下面假设我们希望使用移动语义来完成value
的赋值:
1 | class Annotation { |
但实际上,依然调用的是拷贝构造函数!
因为虽然使用了std::move
将text
确实转换成了右值引用,但是其const
限定依然存在。既然有const
存在,就无法使用string
的移动构造函数,转而使用其拷贝构造函数!
所以,std::move
只能说是将参数转换成了右值引用,至于是否真的执行了移动操作,要根据当前的上下文而定。
理解std::forward
相对于std::move
的无条件转换,std::forward
则是根据情况来判定是否转换。
std::forward
主要是将函数模板参数转为特定的类型,给予不同的函数:
1 |
|
其输出为:
This is process function with const int&
This is process function with int&&
std::forward
根据传入参数是左右引用还是右值引用来判定调用对应版本的重载函数。
std::forward
虽然看起来更加智能,但是其需要参考的条件也多:
- 首先要指定转换的具体类型是什么
- 其次还要参数的类型是右值引用时,才会调用右值引用版本的函数
但std::move
好就好在简单粗暴,不管类型和传入参数,直接转换为右值引用。