深入理解右值引用,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,一经查实,立即删除!

相关文章

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

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

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

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

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

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

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

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

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

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

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

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

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)实现多个线程间的同步操作;当条件不满足时,相关线程被一直阻塞,直到某种条件出现,这些线程才会被唤醒。其主要成员函…

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

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

多线程队列的算法优化

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

linux 文件大小_整理 | Linux下列出目录内容命令

IT服务圈儿有温度、有态度的IT自媒体平台来源:良许Linux(ID:liangxuxiansheng)在 Linux 中,有非常多的命令可以让我们用来执行各种各样的任务。当我们想要像使用文件浏览器一样列出一个目录下的内容时,大家第一时间想到的是 ls 命…

多线程程序中操作的原子性

0. 背景原子操作就是不可再分的操作。在多线程程序中原子操作是一个非常重要的概念,它常常用来实现一些同步机制,同时也是一些常见的多线程Bug的源头。本文主要讨论了三个问题:1. 多线程程序中对变量的读写操作是否是原子的?2. 多…

.net mvc actionresult 返回字符串_ASP.NET Core中的Action的返回值类型

在Asp.net Core之前所有的Action返回值都是ActionResult,Json(),File()等方法返回的都是ActionResult的子类。并且Core把MVC跟WebApi合并之后Action的返回值体系也有了很大的变化。ActionResult类ActionResult类是最常用的返回值类型。基本沿用了之前Asp.net MVC的那…

.jdeveloper_在JDeveloper 12.1.3中为WebSocket使用Java API

.jdeveloper介绍 最新版本的JDeveloper 12c(12.1.3.0)和WebLogic Server 12.1.3一起提供了一些新的Java EE 7功能。 其中之一是对用于WebSocket的JSR 356 Java API的支持。 实际上,从12.1.2.0版本开始就支持WebSocket协议(RFC 645…

为什么程序员需要关心顺序一致性,而不是 Cache 一致性?

本文所讨论的计算机模型是Shared Memory Multiprocessor,即我们现在常见的共享内存的多核CPU。本文适合的对象是想用C 或者Java进行多线程编程的程序员。本文主要包括对Sequential Consistency和Cache Coherence的概念性介绍并给出了一些相关例子,目的是…

canoco5冗余分析步骤_打造高性能的大数据分析平台

大数据时代,大数据的应用与挖掘,大数据的分析和决策,大数据在经济社会的运行轨道上发挥着愈来愈重要的作用。对于大数据分析,现在好多互联网金融公司和传统的商业银行、证券基金公司都非常看重。个个都想在大数据分析中获得重要信…

C 迭代器iterator的实现原理

在经典的设计模式中,有一种迭代器模式,定义为:提供一个对象来顺序访问聚合对象中的一系列数据,而不暴露聚合对象的内部表示。迭代器的主要优点如下:访问一个聚合对象的内容而无须暴露它的内部表示。遍历任务交由迭代器…

html如何在画布上加层,在Canvas中嵌套Html

大概是这样的,现在需要根据一下上传的图片以及一些输入生成图片。本来打算用imagemagick的,但是后来觉得这样前后端要搞两份不同的代码,然后imagemagick使用起来远没有canvas用起来顺手啊。So,最终决定就用Canvas搞定它了&#xf…

如何用illustrator做技术手册_做期货用什么技术指标分析?

来源:期汇股金作者:DC链接:做期货用什么技术指标分析?投资期货市场首先我们要有一套自己的技术分析,那么我们有什么样的技术指标分析最准确呢,没有最准确的技术指标,要看你运用的程度&#xff0…