目的
- 转移move()是为了将左值转换为右值
- 完美转发forward()是为了让实参保持其左值/右值的属性
- 移动语义:将对象的状态或者所有权从一个对象转移到另一个对象,只有转移,没有拷贝。右值引用,move()和移动构造函数都具有移动语义
移动语义可以将资源(堆、系统对象等)通过浅拷贝从一个对象转移到另一个对象这样就能减少不必要的临时对象的创建、拷贝以及销毁,可以大幅提高 C++ 应用程序的性能。
作者: 苏丙榅
链接: https://subingwen.cn/cpp/rvalue-reference/#2-%E6%80%A7%E8%83%BD%E4%BC%98%E5%8C%96
来源: 爱编程的大丙
著作权归作者所有。商业转载请联系作者获得授权,非商业转载请注明出处。
因此需要先了解左值和右值
表示方式
- 左值,右值
左值 l-value locator value:可以取地址的、有明确存储地址的数据;等号左侧
右值 r-value read value:不可以取地址的、只是提供数据值的数据;等号右侧
右值可以分为两种:- 纯右值,非引用返回的临时变量、运算表达式产生的临时变量、原始字面量和 lambda 表达式等
- 将亡值,与右值引用相关的表达式,比如,T&& 类型函数的返回值、 std::move 的返回值等。
- 左值引用,右值引用,常量左值引用,常量右值引用
- 注意左值引用和右值引用的初始化方法不同
- 左值引用和右值引用实质都是起别名
- 右值引用可以让被引用数据的生命周期与右值引用类型的生命周期一致,让该右值“重获新生”
//左值
int num = 9;
//左值引用,通过左值初始化
int& a = num;
//右值:数字,字符串等无法取地址的均为右值
//右值引用
int&& b = 8;
//常量右值引用
const int&& d = 11;
//常量左值引用,可以同类型的各种引用初始化常量左值引用
constexpr int& c = num1;//使用constexptr关键字,必须要将num1声明为全局变量
const int& x = b;
const int& y = a;
const int& z = d;
- move()与forward()
//move() + STL容器
vector<string> vec = {"llx", "wd", "szy"};
//通过move()将vec中的资源转移到res中,减少拷贝次数,提高效率,前提是vec不再使用
vector<string> res = move(vec);
//完美转发
forward<int>(num);
forward<int&>(num);
forward<int&&>(num);
细节
- 右值传递过程中,右值会变为左值,传递具体就是指作为实参进行多次调用
- 编译器会将已命名的右值引用视为左值,将未命名的右值引用视为右值
- move()的转移操作除了对对象,还可以对容器进行,可以大幅提高程序效率
举例
#include <bits/stdc++.h>
using namespace std;
class Test
{
public:
Test() : m_num(new int(100))
{
cout << "construct: my name is jerry" << endl;
printf("m_num地址:%p\n", m_num);
}
Test(const Test& a) : m_num(new int(*a.m_num))
{
cout << "copy construct: my name is tom" << endl;
}
// 添加移动构造函数
Test(Test&& a) : m_num(a.m_num)
{
a.m_num = nullptr;
cout << "move construct: my name is sunny" << endl;
}
~Test()
{
delete m_num;
cout << "destruct Test class ..." << endl;
}
int* m_num;
};
Test getObj()
{
Test t;
return t;
}
Test getObj1(){
return Test();
}
int main()
{
//要求右侧的对象是一个临时对象,才会调用移动构造函数
//如果没有移动构造函数,就会调用拷贝构造函数
Test t = getObj();
cout << endl;
Test&& t1 = getObj();
printf("m_num地址:%p\n", t1.m_num);
//如果没有移动构造函数,使用右值引用初始化要求更高一些
//要求右侧是一个临时的不能取地址的对象
cout << endl;
Test&& t2 = getObj1();
printf("m_num地址:%p\n", t2.m_num);
//Test&& t3 = t2;//error,右值的传递,编译器会将其视为左值
//应用场景
//1.将左值转换为右值
//2.资源转移
Test&& t3 = move(t2);//move()可以将左值变为右值
Test&& t4 = move(t);
//move() + STL容器
vector<string> vec = {"llx", "wd", "szy"};
vector<string> res = move(vec);//通过move()将vec中的资源转移到res中,减少拷贝次数,提高效率,前提是vec不再使用
return 0;
};
运行结果
参考学习
https://www.bilibili.com/video/BV1bX4y1G7ks