深入理解右值引用,move语义和完美转发

move语义

最原始的左值和右值定义可以追溯到C语言时代,左值是可以出现在赋值符的左边和右边,然而右值只能出现在赋值符的右边。在C 里,这种方法作为初步判断左值或右值还是可以的,但不只是那么准确了。你要说C 中的右值到底是什么,这真的很难给出一个确切的定义。你可以对某个值进行取地址运算,如果不能得到地址,那么可以认为这是个右值。例如:

int& foo();foo() = 3; //ok, foo() is an lvalue
int bar();int a = bar(); // ok, bar() is an rvalue

为什么要move语义呢?它可以让你写出更高效的代码。看下面代码:

string foo();string name("jack");name = foo();

第三句赋值会调用string的赋值操作符函数,发生了以下事情:

首先要销毁name的字符串吧

把foo()返回的临时字符串拷贝到name吧

最后还要销毁foo()返回的临时字符串吧

这就显得很不高效,在C 11之前,你要些的高效点,可以是swap交换资源。C 11的move语义就是要做这事,这时重载move赋值操作符

string& string::operator=(string&& rhs);

move语义不仅仅用于右值,也用于左值。标准库提供了std::move方法,将左值转换成右值。因此,对于swap函数,我们可以这样实现:

templatevoid swap(T& a, T& b){    T temp(std::move(a));    a = std::move(b);    b = std::move(temp);}

右值引用

string&& 这个类型就是所谓的右值引用,而把T&称之为左值引用。注意,不要见到T&&就认为是右值引用,例如,下面这个就不是右值引用:

T&& foo = T(); //右值引用auto&& bar = foo; // 不是右值引用

实际上,T&&有两种含义,一种就是常见的右值引用;另一种是即可以是右值引用,也可以是左值引用,Scott Meyers把这种称为Universal Reference,后来C 委员把这个改成forwarding reference,毕竟forwarding reference只在某些特定上下文才出现。

有了右值引用,C 11增加了move构造和move赋值。考虑这个情况:

void foo(X&& x){  // ...}

那么问题来了,x的类型是右值引用,指向一个右值,但x本身是左值还是右值呢?

C 11对此做出了区分:

Things that are declared as rvalue reference can be lvalues or rvalues. The distinguishing criterion is: if it has a name, then it is an lvalue. Otherwise, it is an rvalue.

由此可知,x是个左值。考虑到派生类的move构造,我们因这样写才正确:

Derived(Derived&& rhs):base(std::move(rhs) //std::move不可缺{ ... }

有一点必须明白,那就是std::move不管接受的参数是lvalue,还是rvalue都返回rvalue。因此我们可以给出std::move的实现如下(很接近于标准实现):

template typename remove_reference::type&& move(T&& t) {    using RRefType = typename remove_reference::type&&;    return static_cast(t);}

完美转发

假设有一个函数foo,我们写出如下函数,把接受到的参数转发给foo:

templatevoid fwd(TYPE t){    foo(t);}

我们一个个来分析:

如果TYPE是T的话,假设foo的参数引用类型,我会修改传进来的参数,那么fwd(t)和foo(t)将导致不一样的效果。

如果TYPE是T&的话,那么fwd传一个右值进来,没法接受,编译出错。

如果TYPE是T&,而且重载个const T&来接受右值,看似可以,但如果多个参数呢,你得来个排列组合的重载,因此是不通用的做法。

你很难找到一个好方法来实现它,右值引用的引入解决了这个问题,在这种上下文时,它成为forwarding reference。这就涉及到两条原则。第一条原则是引用折叠原则:

A& & 折叠成 A&A& && 折叠成 A&A&& & 折叠成 A&A&& && 折叠成 A&&

第二条是特殊模板参数推导原则:

1.如果fwd传进的是个A类型的左值,那么T被决议为A&。2.如果fwd传进的是个A类型的右值,那么T被决议为A。

将两条原则结合起来,就可以实现完美转发。

A x; fwd(x); //推导出fwd(A& &&) 折叠后fwd(A&)
A foo();fwd(foo());//推导出fwd(A&& &&) 折叠后 fwd(A&&)

std::forward应用于forwarding reference,代码看起来如下:

templatevoid fwd(T&& t){    foo(std::forward(t));}

要想展开完美转发的过程,我们必须写出forward的实现。接下来就尝试forward该如何实现,分析一下,std::forward是条件cast的,T的推导类型取决于传参给t的是左值还是右值。因此,forward需要做的事情就是当且仅当右值传给t时,也就是当T推导为非引用类型时,forward需要将t(左值)转成右值。forward可以如下实现:

templateT&& forward(typename remove_reference::type& t){    return static_cast(t);}

现在来看看完美转发是怎么工作的,我们预期当传进fwd的参数是左值,从forward返回的是左值引用;传进的是右值,forward返回的是右值引用。假设传给fwd是A类型的左值,那么T被推导为A&:

void fwd(A& && t){    foo(std::forward(t));}

forward实例化:

A& && forward(typename remove_reference::type& t){    return static_cast(t);}

引用折叠后:

A& forward(A& t){    return static_cast(t);}

可见,符合预期。再看看传入fwd是右值时,那么T被推导为A:

void fwd(A && t){    foo(std::forward(t));}

forward实例化如下:

A&& forward(typename remove_reference::type& t){    return static_cast(t);}

也就是:

A&& forward(A& t){    return static_cast(t);}

forward返回右值引用,很好,完全符合预期。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/339165.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

android startanimation 回调,ScheduledThreadPoolExecutor执行莫名停止问题Android几个动画回调运行线程...

本文记录两个问题:ScheduleThreadPoolExecutor莫名停止执行。Animation和Animator两个动画回调监听 运行在哪个线程。一:ScheduleThreadPoolExecutor问题:ScheduledThreadPoolExecutor中scheduleWithFixedDelay(command, initialDelay, delay…

java future用法_纯干货:Java学习过程中的21个知识点和技术点

我们在Java学习过程中要学会抓重点,善于总结,Java学习过程中常见的21个知识点和技术点你知道吗?下面和千锋广州小编一起来看看吧!1. JVM相关对于刚刚接触Java的人来说,JVM相关的知识不一定需要理解很深,对此…

zz测试接口_使用FizzBu​​zz和JUnitParams进行单元测试

zz测试接口我有时使用FizzBu​​zz向新手演示单元测试的基础。 尽管FizzBu​​zz确实是一个简单的问题,但它也可以用于演示更高级的单元测试技术,例如实现参数化测试。 FizzBu​​zz的可能解决方案之一是: public class FizzBuzz {private …

如何优雅地检测类型/表达式有效性?

注1:本文至少需要编译器支持C 11。注2:本文不考虑使用宏。一、老办法在写C 的时候,有时候可能需要检查一个类是否有特定的成员类型,例如:// 检查 T::type 是否存在,存在则 value 为 true,否则为…

html的页面宽高变形问题,CSS+div 设计的网页在不同浏览器中变形的问题

给别人做了一个web,自己这边正常,一到对方,机器上,发现变形了,真郁闷,后来才发现时IE7.0惹得祸,没有办法,学习呗。如何使网页在IE7.0和火狐中不变形?以下的方法可行&…

python爬虫开发 从入门到实战_python网络爬虫从入门到实战开发

1、简单的抓取网页 from urllib import request reqrequest.Request("http://www.baidu.com") responserequest.urlopen(req) htmlresponse.read() htmlhtml.decode("utf-8") print(html) 2、这里补充点Post和Get提交数据的差别 如果使用POST提交表单数据&…

swagger api文档_带有Swagger的Spring Rest API –公开文档

swagger api文档创建API文档后,将其提供给涉众很重要。 在理想情况下,此发布的文档将足够灵活以解决任何最后的更改,并且易于分发(就成本以及完成此操作所需的时间而言)。 为了使之成为可能,我们将利用我在…

html 页面重复度高,哪些情况容易造成重复页面

摘要在很多情况下我们明明没有发布重复页面,但为什么产生重复呢?下面我们一起来看看哪些情况容易造成重复页面?以及我们应该如何处理?abc.comabc.com/index.html在浏览器地址栏中输入这四个不同的URL链接,打在很多情况…

nuxt解决首屏加载慢问题_一个 Node 脚本让你的前端项目加载速度飞起来

写在最前面我的原创什么声明变成什么鬼了……前言随着前端三大框架的盛行,越来越多的前后端分离项目在服务器上跑了起来,随之而来,开发者也慢慢发现了这种开发模式所带来的弊端,其中之一就是首屏加载速度特别慢,因为虽…

数据库连接配置tomcat_Tomcat到Wildfly:配置数据库连接

数据库连接配置tomcat该摘录摘自《 从Tomcat到WildFly 》一书,您将在其中学习如何将现有的Tomcat体系结构移植到WildFly,包括服务器配置和在其顶部运行的应用程序。 WildFly是完全兼容的Java Enterprise Edition 7容器,与Tomcat相比&#xf…

左值、右值、左值引用、右值引用

【导读】:本文主要详细介绍了左值、右值、左值引用、右值引用以及move、完美转发。左值和右值左值(left-values),缩写:lvalues右值(right-values),缩写:rvalues直接上官网…

itex将html转成pdf加水印,itext操作PDF文件添加水印

功能描述:添加图片和文字水印/**** 【功能描述:添加图片和文字水印】 【功能详细描述:功能详细描述】* param srcFile 待加水印文件* param destFile 加水印后存放地址* param text 加水印的文本内容* param textWidth 文字横坐标* param tex…

wxpython界面切换_wxpython实现按钮切换界面的方法

本文实例为大家分享了wxpython按钮切换界面的具体实现代码,供大家参考,具体内容如下 #-*- coding:utf-8 -*- import wx class TestFrame(wx.Frame): def __init__(self): wx.Frame.__init__(self,None,-1,u登陆,size(370,280),stylewx.MINIMIZE_BOX| wx.…

jboss 4.3.0_JBoss BPM Suite 6.0.3版本的5个实用技巧

jboss 4.3.0上周,红帽发布了JBoss BPM Suite的下一个版本,标记为6.0.3 ,已订阅的用户可以在其客户门户中使用。 如果您对本发行版中的新增功能感到好奇,请在客户门户网站上在线查看发行说明和其余文档 。 我们正在寻找一些简单的…

C 条件变量使用详解

condition_variable介绍在C 11中,我们可以使用条件变量(condition_variable)实现多个线程间的同步操作;当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线程才会被唤醒。其主要成员函…

HTML5新布局元素布局,HTML5新的布局元素

HTML5相对于HTML4新增了一些布局元素新增布局标签的优点:⒈更加注重文档的结构内容而不是以什么形式展现出来⒉对人的友好,更加语义化,增加代码的可读性⒊对计算机友好,浏览器更容易解析新增布局标签的内容:新增的布局…

pannel添加的子窗体很大_超简单的地瓜粉焖子做法全解,零失败

总以为做焖子是一项很大的工程,如果朋友知道你会做焖子定会用惊叹的语气崇拜你,想多一项厨艺吗,想让家人吃上自己亲手做的焖子吗?使用艺琳农场的地瓜粉一定让你出手不凡的,超简单,往下看哦新手为了担心霍霍…

swagger api文档_带有Swagger的Spring Rest API –创建文档

swagger api文档使REST API易于使用的真正关键是好的文档。 但是,即使您的文档做得很好,您也需要设置公司流程的权利以正确,及时地发布它。 确保利益相关者按时收到是一回事,但是您也要负责API和文档中的更新。 自动完成此过程可轻…

多线程队列的算法优化

【导读】:本文主要讲解多线程队列的优化。多线程队列(Concurrent Queue)的使用场合非常多,高性能服务器中的消息队列,并行算法中的Work Stealing等都离不开它。对于一个队列来说有两个最主要的动作:添加&am…

购买台式计算机方案,购买电脑的注意事项有哪些?

随着电脑的应用与普及,电脑已经逐渐成为人们学习、工作、生活中不可缺少的工具。同时,电脑的价格在逐渐下降,很多用户开始准备选购自己的电脑。选购电脑要考虑用户的需求、价格承受能力、商家服务质量等。1、明确用户需求购买电脑之前&#x…