C++11 右值引用

C++11 是 C++11的一次 "大版本更新",本文打算聊聊的就是更新内容中的 右值引用。

左值右值

有右就有左,有右值就有左值,那么什么是左值右值呢?

左值(lvalue) :有名字、有内存地址、能被取地址 & 的值 / 表达式

例子:变量、数组元素、解引用的指针

右值(rvalue):没有名字、临时的、不能被取地址的值 / 表达式

例子:字面量、表达式结果、函数返回的临时对象

这个东西看着挺奇怪的,但其实在写代码时一直都见到。

这里 a 是左值,1 是右值。

但是可千万不要当作是 "在左边" 就是左值,"在右边" 就是右值。

这里 a, b 其实都是左值。

左右值看的是 表达式的属性。

右值引用

以前用C++ 通常都是使用的 C++98,里面的 int& ,其实就是左值引用。而 右值引用,是 C++11 才引入的,在 C++98 里面没有,所以需要专门讲。

在传统的左值引用中,我们只能绑定左值。比如:

为了解决这个问题,右值引用便诞生了。

左值引绑定左值,右值引用绑定右值,但其实左值引用也可以绑定右值,右值引用也可以绑定左值。

move 的本质是进行强制类型转换。

右值引用的优势

右值引用被设计出来,那说明原来的左值引用在某些地方无法很好的满足需求。那我们先来了解或者说复习一下左值引用的主要作用。

左值引用的主要使用场景是在函数中左值引用传参 和左值引用传返回值时减少拷贝 ,同时还可以修改实参和修改返回对象的价值 。左值引用其实已经解决了大部分场景的拷贝效率问题,但有些场景是不能使用左值引用返回的。比如:当对象是在函数内部创建的时候,此时你如果想用左值引用该对象并返回,那么会产生悬空引用(野引用)。

如果没有右值引用,我们的解决办法是 返回 值,如果是这样,那我们就无法减少拷贝。那么右值引用是如何解决这个问题的呢?

首先要知道,这里的本质是返回对象是一个局部对象,函数结束这个对象就会被销毁,右值引用也无法改变对象被销毁的事实。但它利用一种特殊的构造函数------移动构造 ,局部对象会被编译器视为亡值(可移动的右值),将内部的堆内存资源转移给返回的对象。

像这段代码中,移动构造就是将 str 在堆中的资源转移给了 ret ,因为堆中的资源不会随着函数结束而销毁,于是就可以在不进行拷贝的情况下拿到函数的返回值。

可能有人会疑惑:test 中的 str 明明是左值啊,怎么变成右值了?前面已经提到:函数返回的临时对象是右值,所以,str 在函数内是一个左值,但是作为函数返回值时,它自动转换为了右值。

还有的疑问是:对于内置类型,没有堆资源,那怎么办呢?对于内置类型,只能使用传值返回,这是不可避免的,但它占用的空间很小,拷贝的开销可以忽略不计。

总结:

1.当返回值不是函数内定义的局部变量时,使用左值引用做返回值。

2.当返回值是 函数内定义的局部变量,并且是 内置类型时,使用 值 做返回值。

3.当返回值是 函数内定义的局部变量,并且不是内置类型时,使用右值 + 移动构造。

类型分类

前面提到了亡值,这其实是右值的一类,下面对 "值" 进行更进一步的分类。

左值:有名字、可取地址、能长久存在

纯右值:没名字、临时、不能取地址

亡值:即将销毁、可以被移动(move出来的)

右值:纯右值 + 亡值

泛左值:左值+亡值

总的来说,其实类型就两层:泛左值和右值。

引用折叠和完美转发

引用折叠

C++ 中不能直接定义引用的引用,比如:

这样写会直接报错,我们只能通过模板 和 typedef 中的类型操作来构成。

通过模板 和 typedef 中的类型操作来构成引用的引用时,C++11给出了一个引用折叠的规则:右值引用的右值引用折叠成右值引用,其他组合均折叠成左值引用。

例子:

也就是只要有一个是左值引用,那就是左值引用,否则为右值引用。

完美转发

首先了解一个概念:变量表达式都是左值属性。意味着:一个右值被右值引用绑定后,右值引用变量表达式的属性也是左值。

例如:

如果我们给 Func 传一个右值, func 得到的是左值。也就是如果给 Func 传入的是 数字 1, func 得到的其实是变量 t ,t 的值为 1 。

当我们希望保持 t 的属性不变,就需要使用到完美转发:

forward 的本质和 move 一样,其实都是强制类型转换。

相关推荐
一水鉴天3 小时前
映射、哈希表与哈斯图:计算机科学的三种基线 20261003(元宝)
开发语言·人工智能
Frank_refuel3 小时前
C++11之一场名为“搬家”的 C++ 之旅
开发语言·c++
Wang's Blog4 小时前
Java 项目实战: 外卖平台优化-Nginx配置文件结构与块层级
java·开发语言·nginx
2601_962071574 小时前
类变量和全局变量的查找路径有什么区别?
开发语言·python
hetao17338375 小时前
2026-10-03~04 hetao1733837 的刷题记录
c++·算法
\光辉岁月/6 小时前
5.java-数组
java·开发语言
Java后端的Ai之路6 小时前
Python进阶探索29_eval内置函数
开发语言·python·探索·eval·内置函数
谢亮_vipxieliang6 小时前
Spring 事务失效的常见场景
java·开发语言·数据库·spring boot
longlongzihan6 小时前
从LeetCode 70. 爬楼梯看动态规划:如何精准推导状态转移方程?
c++·算法·leetcode·动态规划
时间的拾荒人6 小时前
Qt 多线程详解:从 QThread 到实战
开发语言·qt·面试