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

2024-06-04 5767阅读

【C++】C++11右值引用 第1张

👀樊梓慕:个人主页

 🎥个人专栏:《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 

    免责声明:我们致力于保护作者版权,注重分享,被刊用文章因无法核实真实出处,未能及时与作者取得联系,或有版权异议的,请联系管理员,我们会立即处理! 部分文章是来自自研大数据AI进行生成,内容摘自(百度百科,百度知道,头条百科,中国民法典,刑法,牛津词典,新华词典,汉语词典,国家院校,科普平台)等数据,内容仅供学习参考,不准确地方联系删除处理! 图片声明:本站部分配图来自人工智能系统AI生成,觅知网授权图片,PxHere摄影无版权图库和百度,360,搜狗等多加搜索引擎自动关键词搜索配图,如有侵权的图片,请第一时间联系我们,邮箱:ciyunidc@ciyunshuju.com。本站只作为美观性配图使用,无任何非法侵犯第三方意图,一切解释权归图片著作权方,本站不承担任何责任。如有恶意碰瓷者,必当奉陪到底严惩不贷!

    目录[+]