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 一样,其实都是强制类型转换。

相关推荐
李昊哲小课2 小时前
WSL Ubuntu 24.04 GPU 加速环境完整安装指南
c++·pytorch·深度学习·ubuntu·cuda·tensorflow2
Byte不洛2 小时前
C++继承详解(菱形继承与虚拟继承)
c++·继承·面向对象·菱形继承·虚拟继承
ftpeak2 小时前
Python win32底层开发从入门到实战
开发语言·python·win32api
阿正的梦工坊2 小时前
JavaScript 函数组合(Compose & Pipe)详解
开发语言·javascript·网络
闻缺陷则喜何志丹2 小时前
【排序 离散化 二维前缀和】 P7149 [USACO20DEC] Rectangular Pasture S|普及+
c++·算法·排序·离散化·二维前缀和
lly2024062 小时前
Python uWSGI 安装配置
开发语言
君义_noip2 小时前
信息学奥赛一本通 4163:【GESP2512七级】城市规划 | 洛谷 P14921 [GESP202512 七级] 城市规划
c++·算法·图论·gesp·信息学奥赛
不想写代码的星星2 小时前
C++ 的花括号有多狂?std::initializer_list 那些不讲武德的事儿
c++
两年半的个人练习生^_^3 小时前
每日一学:设计模式之原型模式
java·开发语言·设计模式·原型模式