1.右值与右值引用
左值:存储在内存中,有明确地址(可取地址)的数据
右值:可以直接提供数据值的数据,不可取地址
可以对表达式取地址(&)的就是左值,所有有名字的变量和对象都是左值;右值是匿名的
- //左值
- int num = 1;
- //左值引用
- int& a = num;
- //右值
- //右值引用
- int&& b = 5;
- //常量左值引用
- const int& c= num;
- //常量右值引用
- const int&& d = 2;
- //const int&& e = b;//error
- //const int&& f = d;//error
无论左值引用还是右值引用,都必须初始化,都是取别名,都是为了提升效率;右值引用可以延长右值(临时变量)存活周期;左值引用为了避免指针或者值传递时的内存拷贝
右值分为纯右值(数字、字符串、字面量常量、、lambda表达式、非引用返回的临时变量、运算表达式返回的临时变量)与将亡值(与右值引用有关的表达式,如T&&类型的函数返回值、std::move)
- class RightLifecycle
-
- {
- public:
- //浅拷贝
- RightLifecycle() :m_num(new int(100)){
- cout << "构造函数 :RightLifecycle " << endl;
- cout << "m_num的地址: " <<&m_num<< endl;
- }
-
- //拷贝构造函数,深拷贝
- RightLifecycle(const RightLifecycle &xx) :m_num(new int(*xx.m_num)) {
- cout << "拷贝构造函数 :RightLifecycle " << endl;
- }
-
- //移动构造函数(右值引用构造函数)-复用另一个对象的资源(堆内存)
- //m_num浅拷贝
- RightLifecycle(RightLifecycle&& xx) :m_num(xx.m_num) {
- xx.m_num = nullptr;
- cout << "移动构造函数 :RightLifecycle " << endl;
- }
-
- ~RightLifecycle() {
- cout << "析构函数 :RightLifecycle " << endl;
- delete m_num;
- }
-
- int* m_num;
- };
- RightLifecycle getObj() {
- //局部临时变量
- RightLifecycle t;
- return t;
- }
- RightLifecycle getObj1() {
- //临时匿名对象,将亡值
- return RightLifecycle();
- }
- RightLifecycle&& getObj2() {
- //临时匿名对象
- return RightLifecycle();
- }
-
- //调用
- //右侧对象是返回临时变量,移动构造函数才能被调用,否则调用拷贝构造函数
- //没有移动构造函数,会调用拷贝构造函数
- RightLifecycle objx = getObj();
- cout << "普通赋值 m_num的地址: " << objx.m_num << endl;
- cout << endl;
- RightLifecycle&& obj1x = getObj();
- cout << "右值引用 m_num的地址: " << obj1x.m_num << endl;
- cout << endl;
-
-
- //如果没有移动构造函数,使用右值引用的要求会更高些,
- //要求右侧对象是临时对象,同时不能去地址的对象,即返回一个临时匿名对象
- RightLifecycle&& obj2 = getObj1();
- cout << "右值引用 将亡值 m_num的地址: " << obj2.m_num << endl;
- cout << endl;
-
- RightLifecycle &&obj3 = getObj2();
- cout << "右值引用 将亡值 m_num的地址: " << obj3.m_num << endl;
注意:大量申请资源的类应设计移动构造函数,同时提供构造函数,避免移动构造函数出错
模板参数的T &&与自动推导类型的auto &&表示未定的引用类型
通过右值推导的T&&与auto&&表示右值引用
除右值(左值、左值引用、常量左值引用、右值引用、常量右值引用)推导的T&&
与auto&&表示左值引用
const T &&表示右值引用
- template <typename T>
- void ftest_care1(T&& t) {}
- void ftest_care2(const T&& t) {}
- ftest_care1(10);//右值引用
- int x = 10;
- ftest_care1(x);//左值引用
- ftest_care2(x);//右值引用
-
- int x = 100;
- auto && a = x;//左值引用
- auto&& b = 3000;//右值引用
- const auto&& c = 6;//右值引用
编译器会把已命名的右值引用视为左值,把未命名的优质引用视为右值
右值引用在被推导或者传递后,对应的就是一个左值或者右值
- void test_transmit1(int& i) {
- cout << "左值引用" << i << endl;
- }
-
- void test_transmit1(int&& i) {
- cout << "右值引用" << i << endl;
- }
-
- void test_transmitT(int&& k) {
- test_transmit1(k);
- }
-
-
- //调用
- int a = 250;
- test_transmit1(a);
- test_transmit1(222);
- test_transmitT(777);
2.资源转移move
std::move()的作用是初始化右值引用,可以把一个左值转换为右值,是转移没有内存拷贝(与移动构造函数一样,具有移动的意思,把对象状态或者所有权移动到另一个对象)
std::move等效与static_cast
- //资源转移,当ls不使用了,要使用ls2时
- list<string> ls{ "sad","dsfs","wer" };
- list<string> ls1 = move(ls);
-
- //初始化右值引用
- int a = 99;
- int&& b = move(a);
3.完美转发forward
保证右值引用在参数传递时不会变为左值引用。
forward
- template <typename TF>
- void printfforward(TF & tf) {
- cout<<"左值引用 : " << tf<< endl;
- }
-
- template <typename TF>
- void printfforward(TF&& tf) {
- cout << "右值引用 : " << tf << endl;
- }
-
- template <typename TF>
- void printfforwardT(TF&& tfT) {
- //右值引用传参时,根据情况看变为什么类型,这里是左值
- printfforward(tfT);
- printfforward(move(tfT));//左值变为右值
- printfforward(forward<TF>(tfT));//确保不会变换类型
- cout << endl;
- }
-
- //调用
- //forward<T>(t);
- //当t为左值引用时,t将会被转换成一个T类型左值
- //当t为不为左值时,t将会被转换成一个T类型右值
-
- printfforwardT(520);
- int num = 1000;
- printfforwardT(num);
- printfforwardT(forward<int>(num));//当t为不为左值时,t将会被转换成一个T类型右值
- printfforwardT(forward<int&>(num));//当t为左值引用时,t将会被转换成一个T类型左值
- printfforwardT(forward<int&&>(num));//当t为不为左值引用时,t将会被转换成一个T类型右值