首页 > 编程语言 >C++11 右值引用&&、移动语义std::move、完美转发std::forward

C++11 右值引用&&、移动语义std::move、完美转发std::forward

时间:2023-08-26 17:11:32浏览次数:42  
标签:std 11 右值 int data ref size

参考:https://blog.csdn.net/HR_Reborn/article/details/130363997

 

#pragma once
class Array {
public:
    Array() : size_(0), data_(nullptr){

    }
    Array(int size) : size_(size) {
        data_ = new int[size_];
    }

    // 复制构造函数 (深拷贝构造)
    Array(const Array& temp_array) {
        size_ = temp_array.size_;
        data_ = new int[size_];
        for (int i = 0; i < size_; i++) {
            data_[i] = temp_array.data_[i];
        }
    }

    // 赋值构造函数 (深拷贝赋值)
    Array& operator=(const Array& temp_array) {
        delete[] data_;

        size_ = temp_array.size_;
        data_ = new int[size_];
        for (int i = 0; i < size_; i++) {
            data_[i] = temp_array.data_[i];
        }
        //返回对象的引用,是为了做链式处理: f1 = f2 = f3;
        return *this;
    }

    // 移动构造函数
    Array(Array&& temp_array) {
        cout << "移动构造函数\n";
        data_ = temp_array.data_;
        temp_array.data_ = nullptr;// 为防止temp_array析构时delete data,提前置空其data_
    }

    // 移动赋值构造函数
    Array& operator=(Array&& temp_array) {
        cout << "移动赋值构造函数\n";
        data_ = temp_array.data_;
        temp_array.data_ = nullptr;// 为防止temp_array析构时delete data,提前置空其data_
        return *this;
    }


    ~Array() {
        delete[] data_;
    }

public:
    int *data_;
    int size_;
};


// --> 测试 完美转发
void B(int&& ref_r) {
    ref_r = 1;
}

// A、B的入参是右值引用
// 有名字的右值引用是左值,因此ref_r是左值
void A(int&& ref_r) {
    //B(ref_r);  // 错误,B的入参是右值引用,需要接右值,ref_r是左值,编译失败

    B(std::move(ref_r)); // ok,std::move把左值转为右值,编译通过
    B(std::forward<int>(ref_r));  // ok,std::forward的T是int类型,属于条件b,因此会把ref_r转为右值
}

void func1(int& i){
    cout << "参数是左值," << i << endl;
}
void func1(int&& i){
    cout << "参数是右值," << i << endl;
}

void func(int&& i){
    //注意:形参 i是左值
    func1(i);//这样怎么都是调用的 左值引用的 函数。为解决,用完美转发std::forward
}

// 1.如果模板中(包括类模板、函数模板)的参数为 T&& 参数名,那么函数既可以接受左值引用,也可以接受右值引用。
//2. std::forward<T>(参数), 用于转发参数。若参数是左值,转发后仍然是左值,若参数是右值,转发后仍然是右值。
template<class T>
void func(T&& t){
    func1(std::forward<T>(t));
}

// <-- 测试 完美转发

class TestRightValueRef
{
public:
    TestRightValueRef() = delete;
    ~TestRightValueRef() = delete;

    static void testAll(){
        //test02();
        test03();
    }
    static int foo(){ return 0; }
    static void test01(){

        /*
        左值,lvalue,就是赋值符号左边的值,如a=5,a就是左值,但是准确的来说,左值是表达式(不一定是赋值表达式)后仍然存在的持久对象。
        右值,rvalue,右边的值,是指表达式结束就不存在的临时对象。
            纯右值,prvalue,用于计算的或者用于初始化对象的的右值。例如 int a = 5; // 5是右值  注意 字符串 "abc" 这个不是右值,是指针
            ( 在介绍将亡值之前对左右和右值做个总结,有地址的变量就是左值,没有地址的字面值、临时值就是右值。)
            将亡值,xvalue, 是C++11为了引入右值引用而提出的概念,与纯右值的不同点在于,将亡值是即将被销毁、却能够被移动的值。例如,函数中返回的对象。
        有地址的变量就是左值,没有地址的字面值或临时值就是右值。
        */

        /*
        左值引用:只能指向左值。不能指向右值的就是左值引用
        右值引用:只能指向右值,不能指向左值,符号是&&
        */

        int a = 5;// a 是左值, 5是右值
        int b = foo();// b 是左值, foo()是右值

        int& ra = a;        //左值引用
        int& rb = b;        //左值引用
        int&& rc = 5;        //右值引用
        int&& rd = foo();    //右值引用

        //int& r = 5;            //不能指向右值,编译报错
        //int&& r = a;            //不能指向左值,编译报错

        //但const 左值引用  都可以
        const int& rf1 = a;
        const int& rf2 = 3;
        const int& rf3 = foo();
        //为什么?因为const 左值引用不会修改指向值,因此可以指向右值,这也是为什么要使用const &作为函数参数的原因之一,如std::vector的push_back:
        //void push_back(const value_type& val);
        //如果没有const,vec.push_back(5)这样的代码就无法编译通过了。

        //左值引用默认只能指向左值,但是加了const的情况下可以指向右值,那么右值引用有没有类似的机制来指向左值呢?有的,就是std::move。
        //std::move 唯一的功能是把左值强制转换为右值,可以让右值引用指向左值,等于一个强制类型转换。
        int&& rg = std::move(a);
        
        //注意,被声明出来的左、右值引用都是左值,因为被声明出的左右值引用是有地址的,也位于等号左边。
        // 所以,ra,rb,rc,rd 都是左值


        /*
总结:

    从性能上讲,左右值引用没有区别,传参使用左右值引用都可以避免拷贝。
    右值引用可以直接指向右值,也可以通过std::move指向左值;而左值引用只能指向左值(const左值引用也能指向右值)。
    作为函数形参时,右值引用更灵活。虽然const左值引用也可以做到左右值都接受,但它无法修改,有一定局限性

    void f(const int& n) {
        n += 1; // 编译失败,const左值引用不能修改指向变量
    }
     
    void f2(int && n) {
        n += 1; // ok
    }
     
    int main() {
        f(5);
        f2(5);
    }
————————————————
版权声明:本文为CSDN博主「HR_Reborn」的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。
原文链接:https://blog.csdn.net/HR_Reborn/article/details/130363997
        */


    }


    //在实际场景中,右值引用和std::move被广泛应用于在STL和自定义类中实现移动语义,避免拷贝,从而提升程序性能。
    static void test02(){
        // 例1:Array用法
        Array a(2);

        // 做一些操作
        //.....

        // 左值a,用std::move转化为右值
        Array b(std::move(a));//会调用移动构造函数
        //std::move(a) 后 a 不应再被使用,会报错。因为nullptr
        //b = a;    //error

        Array c;
        Array d;
        c = d;//调用赋值构造函数

        c = std::move(d);//调用移动赋值构造函数

    }

    static void test03(){
        //完美转发
        /*
        完美转发std::forward

        和std::move一样,std::forward本质上是进行类型转换,即左值右值之间的转换,与move相比,forward更强大,move只能转出来右值,forward都可以。

        std::forward<T>(u)有两个参数:T与 u。 
        a. 当T为左值引用类型时,u将被转换为T类型的左值; 
        b. 否则u将被转换为T类型右值。

        举个例子,有main,A,B三个函数,调用关系为:main->A->B。

        */
        int a = 5;
        A(std::move(a));

        cout << "-------------\n";
        func(a);
        func(5);
    }
};

 

标签:std,11,右值,int,data,ref,size
From: https://www.cnblogs.com/htj10/p/17659118.html

相关文章

  • 关于自建Rustdesk 远程桌面服务器的公网访问:无法连接中继服务器的问题解决方法
    自建服务器位于内网时,内网客户端ID/中继的地址通常写成内网IP,外网客户端一般会用公网IP进行端口映射,但这样设置出现外网客户端无法连接中继服务器,但内网客户端使用正常的现象。经过半天的排查分析,当内网和外网填写的自定义服务器地址时,rust服务器无法识别出需要使用nat包的地址,默......
  • xsschallenge通关(11-15)
    level11老规矩,先查看源码,做代码审计:<?phpini_set("display_errors",0);$str=$_GET["keyword"];$str00=$_GET["t_sort"];$str11=$_SERVER['HTTP_REFERER'];$str22=str_replace(">","",$str11);$str33=st......
  • 1152 Google Recruitment
    题目:InJuly2004,GooglepostedonagiantbillboardalongHighway101inSiliconValley(showninthepicturebelow)forrecruitment.Thecontentissuper-simple,aURLconsistingofthefirst10-digitprimefoundinconsecutivedigitsofthenaturalcon......
  • P3521 [POI2011] ROT-Tree Rotations
    P3521[POI2011]ROT-TreeRotations首先合并两棵子树的时候只关心子树内值的个数,并不关心子树内具体是什么顺序,引导从下向上线段树合并计算代价。每一个值只会出现一次,首先每个叶子节点开一棵动态开点值域为\(1-n\)的线段树维护,初始只有自己的值的位置为\(1\)。然后对于每......
  • P1113 杂务 (DAG拓扑排序--DP)
    这是一道拓扑排序的模板题0额.所需的前置知识:图论相关的基本概念建图,存图图的遍历非常入门的DP下面进入正文1引入拓扑排序是一类用于处理DAG(Directedacyclicgraph),即有向无环图上的问题。以这道题为例,我们分析拓扑排序的作用:显然地,本题中各项工作是有一定的依......
  • Windows11隐藏屏幕下方的白线:TranslucentTB软件
    问题引出:win11在设置中打开自动隐藏任务栏之后,有一条白线,看着很是烦人解决问题:使用Github开源项目Translucent隐藏这条白象1.可以在微软商店安装,如下图2.也可以点击链接到此页面下载TranslucentTB.appinstaller然后安装3.这个软件体积很小,占用的内存也很小,可以设置开机自动......
  • [刷题记录Day11]Leetcode
    No.1题目有效的括号思路奇数个符号一定不符合分析括号不匹配的可能性第一种情况,字符串里左方向的括号多余了,所以不匹配![[brackets0.png]]第二种情况,括号没有多余,但是括号的类型没有匹配上![[brackets1.png]]第三种情况,字符串里右方向的括号多余了,所以不匹配![[brac......
  • 《C++》C11新特性--1
    1.原始字符串字面量R"(字符串)"constchar*str1="D:hello\world\test.txt";constchar*str2=R"(D:hello\world\test.txt)";std::cout<<"直接输出str:\t\t"<<str1<<std::endl;std::cout<......
  • 1154 Vertex Coloring
    题目A propervertexcoloring isalabelingofthegraph'sverticeswithcolorssuchthatnotwoverticessharingthesameedgehavethesamecolor.Acoloringusingatmost k colorsiscalleda(proper) k-coloring.Nowyouaresupposedtotellifagi......
  • 牛客练习赛114 D题题解
    比赛编号太臭了题目链接对一第一组数据,我们形象化的得到下图:如何拆解使其变成一个“顺子”呢,我们像这样:贪心的从后往前取,对于取数列时的终点也就是枚举的起点如果不为0,就向前一直取,如果取到一个数\(x\)发现这个数的个数\(num_x\)是大于\(num_{x-1}\)的,就停止,最后看拆......