ICode9

精准搜索请尝试: 精确搜索
首页 > 编程语言> 文章详细

C++右值引用

2022-02-25 10:03:31  阅读:166  来源: 互联网

标签:std 右值 int 左值 C++ 引用 size


 

左值、右值

1 左值是表达式结束后依然存在的持久对象

2 右值是表达式结束后不再存在的临时对象

简单来说,能取地址的是左值,否则就是右值。

 

右值引用的意义

实现移动语义完美转发

 

移动语义

C++11的右值引用和std::move可以实现移动语义,通过减少拷贝操作提升效率

class A{

public:
    A(): size(0), data(nullptr){}
    A(size_t size): size(size){
        data = new int[size];
    }

    A(const A& rhs){
        size = rhs.size;
        data = new int[size];
        for(int i=0; i<size; ++i){
            data[i] = rhs.data[i];
        }
        cout << "Copy constructor" << endl;
    }

    A& operator=(const A& rhs){

        if(this == &rhs){
            return *this;
        }

        delete[] data;

        size = rhs.size;
        data = new int[size];
        for(int i=0; i<size; ++i){
            data[i] = rhs.data[i];
        }
        cout << "Copy assignment" << endl;
        return *this;
    }


    ~A(){
        delete[] data;
    }

private:
    int *data;
    size_t size;
};

对于上面的类来说,无论是拷贝构造还是拷贝赋值,都需要将源数据一一拷贝,但是如果源数据在拷贝之后就不需要了,我们直接把源数据拿过来不是省事多了吗?对此我们只需要修改一下指针地址即可,在代码中加入下面的移动构造函数。

A(A&& rhs){
    data = rhs.data;
    size = rhs.size;
    rhs.size = 0;
    rhs.data = nullptr;
    cout << "Move constructor" << endl;
}

于是通过std::move将源数据转化为右值后就会调用相应的移动构造函数。

int main(){
    A a1 = A(10);
    A a2(std::move(a1));
}

 

完美转发

首先看第一个例子:

#include<iostream>
using namespace std;

int main(){
    int x = 5;
    int& left = x;
    int&& right = std::move(x);
    //cout << &left << endl << &right << endl;
}

在上面的代码中,被声明的左值引用和右值引用都是左值,是可以输出它们的地址的。

 

然后看二个例子:

#include<iostream>
using namespace std;

void func(int& x){
    cout << "左值" <<endl;
}

void func(int&& x){
    cout << "右值" <<endl;
}

void test(int&& x){
    func(x);
}

int main(){
    test(5);
}

在上面的代码中,最后的输出是左值,虽然传给 test 函数的是一个右值,然而在调用 func 时,使用的是 x,参数有了名称,联系第一个例子,此时 x 是一个左值。

如果想要最后的输出是右值,需要使用 std::forward,而 std::forward 中涉及到了引用折叠。

 

引用折叠

对于T&、T&&来说,其引用类型未定,可能是左值引用,也可能是右值引用,需要视传入的实参而定。

T& &  T& 左值引用的左值引用折叠为左值引用
T& && T& 右值引用的左值引用折叠为左值引用
T&& & T& 左值引用的右值引用折叠为左值引用
T&& && T&& 右值引用的右值引用折叠为右值引用

简而言之,只有两者均为右值引用时,最后的引用类型才为右值引用,否则均为左值引用

 

源码分析

 remove_reference 源码

template<typename _Tp>
struct remove_reference
{ typedef _Tp   type; };
 
// 特化版本
template<typename _Tp>
struct remove_reference<_Tp&>
{ typedef _Tp   type; };
 
template<typename _Tp>
struct remove_reference<_Tp&&>
{ typedef _Tp   type; };

std::forward 源码

template<typename _Tp>
constexpr _Tp&&
forward(typename std::remove_reference<_Tp>::type& __t) noexcept
{ return static_cast<_Tp&&>(__t); }

根据上述源码,首先通过remove_reference获取 _Tp 的类型 type,然后声明左值引用变量 __t 。

根据 _Tp 的不同,_Tp 会发生引用折叠:

  1. 当 _Tp 为左值引用时,_Tp折叠为左值引用
  2. 当 _Tp 为右值引用时,_Tp折叠为右值引用

可以发现当 std::forward 的输入是左值引用时,输出也是左值引用;输入是右值引用时,输出也是右值引用。

 

在下面的代码中,使用了 std::forward 之后就会输出右值。

#include<iostream>
using namespace std;

void func(int& x){
    cout << "左值" <<endl;
}

void func(int&& x){
    cout << "右值" <<endl;
}

void test(int&& x){
    func(std::forward<int>(x));
}

int main(){
    test(5);
}

  

 

 

References:

  1. 一文带你详细介绍c++中的std::move函数
  2. C++ 理解std::forward完美转发
  3. 一文读懂C++右值引用和std::move

标签:std,右值,int,左值,C++,引用,size
来源: https://www.cnblogs.com/zyb993963526/p/15933775.html

本站声明: 1. iCode9 技术分享网(下文简称本站)提供的所有内容,仅供技术学习、探讨和分享;
2. 关于本站的所有留言、评论、转载及引用,纯属内容发起人的个人观点,与本站观点和立场无关;
3. 关于本站的所有言论和文字,纯属内容发起人的个人观点,与本站观点和立场无关;
4. 本站文章均是网友提供,不完全保证技术分享内容的完整性、准确性、时效性、风险性和版权归属;如您发现该文章侵犯了您的权益,可联系我们第一时间进行删除;
5. 本站为非盈利性的个人网站,所有内容不会用来进行牟利,也不会利用任何形式的广告来间接获益,纯粹是为了广大技术爱好者提供技术内容和技术思想的分享性交流网站。

专注分享技术,共同学习,共同进步。侵权联系[81616952@qq.com]

Copyright (C)ICode9.com, All Rights Reserved.

ICode9版权所有