小工具知识网

当前位置: 首页 > 行业知识 > 【C++】C++11右值引用

行业知识

【C++】C++11右值引用

2026-11-02 17:24:14 来源:互联网转载

樊梓慕:个人主页

 个人专栏:《C语言》《数据结构》《蓝桥杯试题》《LeetCode刷题笔记》《实训项目》《C++》《Linux》《算法》

每一个不曾起舞的日子,都是对生命的辜负


目录

前言

1.什么是左值&&什么是右值

左值

右值

2.什么是左值引用&&什么是右值引用

左值引用

右值引用

3.左值引用与右值引用的比较

左值引用总结

右值引用总结

4.右值引用的使用场景和意义

传值返回场景分析

移动构造

移动赋值

总结

容器的插入场景分析

move的简单解释 

右值被右值引用后,该右值引用是左值 

那为什么这样设计呢?

5.完美转发

万能引用

完美转发保持值的属性不变


前言

今天我们正式进入C++11的学习,C++11引入的一个非常重要的语法就是右值引用,在C++11之前的C++版本我们所提的引用都是左值引用,那么右值引用与左值引用又有什么区别呢?什么是左值?什么是右值?右值引用的价值体现在哪里?以及完美转发和万能引用的相互配合?那么接下来我们就来学习有关右值引用的相关知识。


欢迎大家收藏以便未来做题时可以快速找到思路,巧妙的方法可以事半功倍。 

=========================================================================

GITEE相关代码:樊飞 (fanfei_c) - Gitee.com

=========================================================================


1.什么是左值&&什么是右值

左值

左值是一个表示数据的表达式,如变量名或解引用的指针。

  • 我们可以获取左值的地址,一般情况下也可以被修改(const修饰的左值除外)。
  • 左值既可以出现在赋值符号的左边,也可以出现在赋值符号的右边。
    int main(){//以下的p、b、c、*p都是左值int* p = new int(0);int b = 1;const int c = 2;return 0;}

    右值

    右值也是一个表示数据的表达式,如字母常量、表达式的返回值、函数的返回值(不能是左值引用返回)等等。

    • 不可以获取右值的地址。
    • 右值可以出现在赋值符号的右边,但是『 不能』出现在赋值符号的左边。
      int main(){double x = 1.1, y = 2.2;//以下几个都是常见的右值10;x + y;fmin(x, y);//错误示例(右值不能出现在赋值符号的左边)//10 = 1;//x + y = 1;//fmin(x, y) = 1;return 0;}

      其实右值一般都是一个临时变量或常量值,比如代码中的10就是常量值,表达式x+y和函数fmin的返回值就是临时变量,这些都叫做右值,而我们知道这些临时变量和常量值实际上并没有被存储起来,当然也就不存在地址。

      //这里x是左值int func1(){static int x = 0;return x;}//这里x是左值int& func2(){static int x = 0;return x;}
      • 当返回值没有引用标记时,返回的是临时拷贝x的一份临时变量;
      • 当返回值有引用标记时,返回的是x本身(注意销毁的问题)。

        2.什么是左值引用&&什么是右值引用

        传统的C++语法中就有引用的语法,而C++11中新增了右值引用的语法特性,为了进行区分,于是将C++11之前的引用就叫做左值引用。但是无论左值引用还是右值引用,本质都是给对象取别名。

        左值引用

        左值引用就是对左值的引用,给左值取别名,通过“&”来声明。比如:

        int main(){//以下的p、b、c、*p都是左值int* p = new int(0);int b = 1;const int c = 2;//以下几个是对上面左值的左值引用int*& rp = p;int& rb = b;const int& rc = c;int& pvalue = *p;return 0;}

        右值引用

        右值引用就是对右值的引用,给右值取别名,通过“&&”来声明。比如:

        int main(){double x = 1.1, y = 2.2;//以下几个都是常见的右值10;x + y;fmin(x, y);//以下几个都是对右值的右值引用int&& rr1 = 10;double&& rr2 = x + y;double rr3 = fmin(x, y);return 0;}

        很多人到这里就有疑惑了,引用的本质就是起别名,但是右值我们知道是没有地址的,如果一个引用可以标记在右值上,那又有什么意义呢?

        是的,既然有右值引用存在,那么右值引用一定是将这个临时变量存放到了某个确定的地址上,让这个右值可以被取到地址,并且可以被修改,当然如果不想让被引用的右值被修改,可以用const修饰右值引用。比如:

        int main(){double x = 1.1, y = 2.2;int&& rr1 = 10;const double&& rr2 = x + y;rr1 = 20;rr2 = 5.5; //报错return 0;}

        3.左值引用与右值引用的比较

        左值引用总结

        • 左值引用只能引用左值,不能引用右值。
        • 但是const左值引用既可引用左值,也可引用右值。
          int main(){// 左值引用只能引用左值,不能引用右值。int a = 10;int& ra1 = a; // ra为a的别名//int& ra2 = 10; // 编译失败,因为10是右值// const左值引用既可引用左值,也可引用右值。const int& ra3 = 10;const int& ra4 = a;return 0;}

          右值引用总结

          • 右值引用只能右值,不能引用左值。
          • 但是右值引用可以move以后的左值。
            int main(){// 右值引用只能右值,不能引用左值。int&& r1 = 10;// error C2440: “初始化”: 无法从“int”转换为“int &&”// message : 无法将左值绑定到右值引用int a = 10;int&& r2 = a;// 右值引用可以引用move以后的左值int&& r3 = std::move(a);return 0;}

            4.右值引用的使用场景和意义

            在探究右值引用的使用场景和意义之前,我们来回忆以下左值引用给我们带来的优点:

            左值引用可以避免一些没有必要的拷贝操作,比如传参或函数返回值。

            但是左值引用在修饰函数返回值时却容易出现问题,因为函数返回值是一个的局部变量,出了函数作用域就被销毁了,如果给加上了左值引用,就会导致左值引用出现问题,所以这种情况下不能使用左值引用作为返回值,只能以传值方式返回,这就是『 左值引用的短板』。

            既然是右值,我们就可以使用右值引用,但是右值引用解决这里的问题是『 间接解决的』。

            什么叫间接解决??

            右值引用不能直接加到返回类型上直接解决么,答案当然是不能的,因为不管你给返回值加左值引用还是右值引用,都改变不了它即将被销毁的事实。

            所以我们只能间接解决,怎么间接解决呢?

            传值返回场景分析

            移动构造

            我们想要避免拷贝构造的发生,那就要设法让编译器在遇到右值引用时调用其他构造方式,这里采用的就是『 移动构造』。

            而移动构造说白了就是利用swap函数将『 将亡值』与当前对象进行交换,获得『 将亡值』的数据,通过一个swap即可得到数据,不需要调用拷贝构造既节省了时间也节省了空间。

            这种swap其实是一种非常危险的行为,只能适用于『 将亡值』,可以理解为是一种资源的掠夺。 

            将亡值:即将销毁的变量,比如返回值x这种。

            增加移动构造之后,由于移动构造采用的是右值引用接收参数,因此如果拷贝构造对象时传入的是右值,那么就会调用移动构造函数(编译器最匹配原则)。

            比如:

            // 拷贝构造 -- 左值string(const string& s):_str(nullptr){cout 

上一篇:【面试题解析--Java基础】回顾与加深,浅浅回顾JAVA常规八股,利用起碎片化时间。

下一篇:野生蓝莓清洗秘籍,轻松享受自然美味,洗净尽享新鲜!