深入探索C++对象模型一书中拷贝构造函数和NRV关系探讨

深度解读《深度探索C++对象模型》之C++返回值优化 返回值优化是什么意思?编译器在什么情况下会启用返回值优化,返回值优化能获得多少性能的提升?它有什么优缺点?本篇将从背后的机制来深入分析。 阅读详情

转自:http://blog.guorongfei.com/2016/01/11/cpp-copy-constructor-nrv/

最近深入探索C++对象模型一书,对于P67中最后一段话的第一句非常不解

这个程序的第一个版本不能实施 NRV 优化,因为 test class 缺少一个 copy constructor

从这段文字来看如果没有拷贝构造函数就不会有 NRV 优化,这一点让人颇为不解,因为从 P66 页中给出的例子来看,NRV 通过额外的引用型参数优化掉了参数的返回,根本没有拷贝构造函数的调用,这个代码和作者自己的论述看上去是自相矛盾的。

对于这个问题,我在三更_雨的博文第二章构造函数语义学--关于NRV优化和copy constructor,找到一些相关的答案,调整了文章行文顺序,放在此处做一个记录。

作者的观点

这个解释来自作者李普曼:

早期的cfront需要一个开关来决定是否应该对代码实行NRV优化,这就是是否有客户(程序员)显式提供的拷贝构造函数:如果客户没有显示提供拷贝构造函数,那么cfront认为客户对默认的逐位拷贝语义很满意,由于逐位拷贝本身就是很高效的,没必要再对其实施 NRV优化;但如果客户显式提供了拷贝构造函数,这说明客户由于某些原因(例如需要深拷贝等)摆脱了高效的逐位拷贝语义,其拷贝动作开销将增大,所以将应对其实施NRV 优化,其结果就是去掉并不必要的拷贝函数调用。

这说明其实作者之所以这么说是因为cfront编译器的缘故,不过目前这种编译器并不多见,所以这个论述本身也就不成立了,读者可以直接忽略这个观点。

译者的观点

作者既然给出了解释,其他的解释原本没有太大的必要,但是这些观点对于书的理解有一定的帮助,所以也纪录在此。

这本书的译者侯捷先生在他的 FAQ 中对于这个问题有和读者之间的讨论。过程如下:

leetron给侯捷写信说道:

问题:在67页,最下面两行:这个程式的第一个版本不能实施NRV最佳化,因为test class 缺少一个copy constructor。但是在66页「在编译器层面做最佳化」那一段中所列的码显示,当编译器把xx以__result取代,变成__result.X::X(); 即default constructor被唤起。唤起default constructor 是可以理解的,可是编译器转换后的码并没有使用到 copy constructor呀,为什麽67页最后两行却说缺少一个 copy constructor,就不能实施这个最佳化了呢?

我对上面这个问题做了些解释,但不知我的猜想是否正确。

我的解释是:如同63页与64页「回返值的初始化」这一段,编译器可能将 63页下面的 X bar()函式定义转换成64页的虚拟码,其中有一行__result.X::X(xx); 这会使用到copy constructor。

转换成64页的码后,65页与66页分述了两种后续可能出现的最佳化动作,其中一种即是66 页的编译器层面做最佳化。如此,虽然66页最佳化后的码看起来并不使用到copy constructor,但是这些码是根据像64页那种样子的码(注一)最佳化而来的,而若没有 copy constructor,根本无法转换成64页那种虚拟码,因为其中有一个呼叫copy constructor的动作。所以,虽然 66页经过编译器最佳化的结果省去了 __result.X::X(xx); 这个copy constructor的呼唤动作(因为根本没有xx了),但若没有明白提供一个copy constructor,却无法让编译器进行这样的最佳化。

另一方面,我叁考第5章,205页最下面一段话:「一般而言如果你的设计之中,有许多函式都需要以传值(by value) 传回一个local class object....那麽提供一个copy constructor 就比较合理--甚至即使default memberwise语意已经足够。它的出现会触发 NRV最佳化。然而,就像我在前一个例子中所展现的那样,NRV最佳化后将不再需要唤起 copy constructor,因为运算结果已经被直接计算於「将被传回的object」体内了。」所以,我提出如上所述那个解释,但不确定是否正确,所以e-mail给您以确认一下。

注一:当然,编译器到底怎麽实作这些转换动作,理论上我们是未知的,不能一概而论。所以我写「像64页那种样子的码」。

侯捷给出的答复是:

首先,我要说 leetron 把他的意思描述得非常清楚。在我收到的读者来函中,算是上品— 尤其是描述这麽复杂的思路。

其次,我同意 leetron 说:

转换成64页的码后,65页与66页分述了两种后续可能出现的最佳化动作,其中一种即是 66页的编译器层面做最佳化。

但是我不同意 leetron 这样的看法:

如此,虽然66页最佳化后的码看起来并不使用到copy constructor,但是这些码是根据像64页那种样子的码(注一)最佳化而来的,

我认为,NRV 最佳化并非是由 p63 的原始码而至 p64 的虚拟码,再至 p66 的最佳化。我认为是从 p63 的原始码直接至 p66 的最佳化。所以,似乎可以不需要 copy ctor。

但这麽一来我也无法解释为什麽 lippman 在 p67 最下强调「必须要有 copy ctor 才能实施 NRV 最佳化」。

最后侯捷引用了另外一个读者的论述

黄俊达先生认为:Lippman 在 p67 最后一行所言『这个程式的第一个版本不能实施 NRV 最佳化,因为 test class 缺少一个 copy constructor』,此语错误。黄先生认为如果程式没有 explicit copy constructor,编译器会自动为我们做出来(如为 trivial,则直接 bitwise copy;如为 nontrivial,则由编译器为我们合成出一个 copy constructor )。因此,有没有 explicit copy constructor 并不影响 NRV 最佳化的实施。他认为 NRV 最佳化主要是由编译器 option 来决定要不要实施。他并且做了一些实验,判断 VC 和 gcc 都没有做到 NRV 最佳化,而其不做的理由不是因为技术上的困难,是为了避免造成「user defined copy constructor 之副作用失效」-- 所谓副作用是指,例如「在 user defined copy constructor 中做一个 cout 输出」之类这种「与 memberwise copy 无关」的动作。


自己动手理解NRV优化 自己动手理解NRV优化2010.6.29一、NRV的简单理解    NRV是Nameed Return Value的简称。NRV优化简单的说:有一条语句,A a = f();其中f()是一个函数,函数里边申请了一个A的对象b,然后把对象b返回。在对象返回的时候,一般情况下要调用拷贝函数,把函数f()里边的局部对象b拷贝到函数外部的对象a。但是如果用了NRV优化,那就不必要调用拷贝构造函数,编译器可以这样做,把a的地址传递进函数f(),然后不让f()申请要返回的对象b的空间,用a的地址来代替b的地址,这样当要 阅读详情

相关推荐

深度探索C++对象模型笔记

一、关于对象C 语言是程序性的,语言本身并没有支持数据和函数之间的关联性C++ 中可能采取抽象数据类型,或者是多层次的类结构完成C++ 的封装并没有增加多少成本,每一个成员函数虽然在cla...

编程与实战的博客 1811

C++对象模型的那些事儿之五:NRV优化和初始化列表

前言在C++对象模型的那些事儿之四:拷贝构造函数中提到如果将一个对象作为函数参数或者返回值的时候,会调用拷贝构造函数,编译器是如何处理这些步骤,又会对其做哪些优化呢?本篇博客就为他家介绍一个编译器的优化操作:NRV,以及关于初始化列表的一些容易踩的“坑”!NRVNRV是named Return Value的缩写,翻译过来就是具名返回值优化,这个优化到底在编译层干了些什么事,我们先来看个例子:clas

ZeeCoder 876

华为ITR流程实战指南:从设计到落地的全流程解析(附完整方案下载)

本文深度解析华为ITR流程从设计到落地的全流程实战指南。ITR不仅是高效处理客户问题的“救火”系统,更是驱动产品改进、预防问题的企业“免疫系统”。文章详细拆解了流程设计的四步走策略、三层技术支持体系的组织保障、以闭环改进为核心的考核激励,并探讨了如何借鉴其精髓,优化自身问题管理,实现从被动响应到主动服务的跨越。

y2z3a4b5c的博客 378

C++构造函数、拷贝构造函数、赋值运算符漫谈(三)——NRV

//首先看下面一段程序:class X { public: X() { cout<<"X()"<<endl; }; X(int v):val(v) { cout<<"X(int)"<<endl; } X(const X& x) { cout<<"X(const X& x)"<<endl; } X& operator=(const X&) { cout<<"="

lvyilong316的专栏 840

手把手教你用UNI-MOL进行分子性质预测:从3D构象到实战结果

本文提供了使用UNI-MOL进行分子性质预测的完整实战指南。通过详细的环境搭建、3D构象生成、模型加载与微调步骤,手把手教你构建端到端的预测流水线。文章重点解析了如何利用UNI-MOL的3D分子表示学习能力,超越传统2D方法,实现对溶解度、活性等性质的更精准预测,并探讨了结果解读与模型分析方法。

weixin_29164091的博客 116

Win11实时字幕还能这么玩?手把手教你打造专属‘AI字幕机’,告别视频网站无字幕困扰

本文详细介绍了如何利用Win11的实时字幕功能打造全平台通用的AI字幕系统。通过C#抓取程序、Node.js中转服务和前端展示界面的组合,实现自动抓取系统字幕并实时推送到任意网页视频的方案,有效解决在线视频无字幕的困扰。

weixin_33735077的博客 391

理解NRV优化

大纲:  函数返回局部对象的拷贝的一般实现方式。 NRV(Named Return Value)优化。 NRV优化触发的疑问。 一、函数返回局部对象的拷贝的一般实现方式 比如有这么一段函数定义: class X; X bar() { X x1; // 处理 x1.. return x1; } 在学习C++语法时,我们知道了。针对”Xbar()”这样的函数

麻辣丝瓜的世界 7721

你的OLED屏和MPU6050为啥通信失败?从IIC的7位地址冲突讲起

本文深入解析OLED屏与MPU6050传感器在I2C总线上的通信故障,重点探讨7位地址冲突问题。通过分析I2C协议中的地址分配机制,提供硬件调整和软件调试的完整解决方案,帮助开发者有效排查和解决设备无响应问题。

weixin_26876073的博客 151

C++ NRV 析构函数也可以控制?

根据深度探索C++对象模型中的说法,如果类未定义拷贝构造且编译器也不合成拷贝构造,编译器不会对这个类施加NRV优化,即函数中声明的局部变量类实例,在return到类外部的时候,会进行一次拷贝,如果有拷贝构造,不管是合成的还是用户主动定义的,这个时候都不会拷贝,因此我写了如下测试代码,注:测试版本为g++ 13.2.0。的,与学习的理论冲突了,检查类的实现发现没有成员变量,没有继承,没有虚函数,也没有虚继承,不应该合成复制构造函数,将构造和析构都去掉试试,此时的类testmove为。试试,结果还是一样,

weixin_39130561的博客 162

手把手教你逆向京东搜索接口:H5ST 556位参数生成与V8补环境技巧

本文深入解析了京东搜索接口中关键的H5ST 556位参数生成机制,并提供了详细的逆向实战指南。针对其复杂的反爬虫环境检测,文章重点介绍了通过链式补环境技巧,在Node.js中完整模拟浏览器环境(包括原型链检测和指纹验证)的方法,帮助开发者成功绕过防护并生成有效参数。

weixin_29315091的博客 483

【网络】PFC背景和原理 、文档(DCB=PFC + ETS,DCBX=DCB扩展)

目录 零、背景 一、PFC 1.概述 2.报文格式 3.工作机制 二、ETS 产生原因 基本原理 优先级组的调度 优先级的调度 三、DCBX 产生原因 基本原理 DCBX TLV介绍 五、PFC 拥塞研究(文档收集) 六、DC-QCN算法 (网络工程师必会) 7.1 α更新 7.2 降速 7.3 提速 8.总结 ...

小鱼菜鸟的博客 2165

解释:《深度探索C++对象模型》对NRV优化的讨论

原文地址:http://blog.youkuaiyun.com/zha_1525515/article/details/7170059 感谢作者! 大纲:  函数返回局部对象的拷贝的一般实现方式。 NRV(Named Return Value)优化。 NRV优化触发的疑问。 一、函数返回局部对象的拷贝的一般实现方式 比如有这么一段函数定义:

hujing_Liu 2105

相似贴子推荐:基于 LangChain4j + Milvus 的混合检索实战

本文提出了一种基于LangChain4j和Milvus的混合检索方案,用于解决论坛社区中的相似帖子推荐问题。该方案结合稠密向量语义检索和BM25关键词检索的优势,通过RRF算法实现结果融合。系统采用bge-small-zh模型生成语义向量,利用Milvus 2.5+原生支持的BM25功能和混合搜索API,在百万级数据规模下实现50ms内的检索延迟,召回率和精确率分别提升15%和22%。文章详细介绍了从数据预处理、向量化到混合检索的完整实现流程,并探讨了进阶优化方向,为推荐系统和RAG应用提供了高效解决方案。

分享AI、区块链技术及应用,未来已来 88

深度探索C++对象模型之美

从概念来上来讲,每一个没有定义构造函数的类都会由编译器来合成一个默认构造函数,以使得可以定义一个该类的对象,但是默认构造函数是否真的会被合成,将视是否有需要而定。C++ standard 将合成的默认构造函数分为 trivial(不重要) 和 notrivial(重要) 两种,前文所述的四种情况对应于notrivial默认构造函数,其它情况都属于trivial。对于一个trivial默认构造函数,编译器的态度是,既然它全无用处,干脆就不合成它。

谢谢大家的关注 3494

基于UC3842的反激电源的全面深入解析与MATLAB仿真

内置电流检测,可实现电流模式控制,提高系统的动态响应性。多种保护功能,如欠压锁定、过温保护、短路保护等,确保系统的安全可靠运行。宽工作电压范围,适应不同的电源设计需求。简单的外部元件配置,易于实现。UC3842的典型应用包括开关电源、DC-DC转换器和反激式电源等,其稳定性和可靠性在实际应用中得到了广泛验证。

qq_38334677的博客 1086

IEM模型,地表粗糙度模拟

利用微波反演土壤水分时候,利用IEM模型可以实现对随机地表的模拟

拷贝构造函数与NRV优化

《深度探索C++对象模型》第二章67页有: 这个程序的第一个版本不能实施NRV优化,因为test class 缺少一个copy constructor. 也就是缺少拷贝构造函数所以不能NRV优化。 NRV优化和拷贝构造函数是有关系的,只有定义了拷贝构造函数才会开启NRV优化,但现代编译器NRV优化的开启一般都与拷贝构造函数没有关系, 早期的 cfront需要一个开关来决定是否应该对代码实行...

speargod的博客 379

Probability and Statistics - 4th edition

Probability and Statistics - 4th edition

GCC编译器下NRV优化

关于NRV优化的相关测试以及思考

weixin_46715151的博客 973

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

深入探索C++对象模型-构造函数 阅读笔记

深入探索C++对象模型-构造函数 阅读笔记 文章目录深入探索C++对象模型-构造函数 阅读笔记默认构造函数什么情况下编译器需要合成默认构造函数?注意拷贝构造函数什么情况下编译器需要合成拷贝构造函数?注意具名返回值(Named Return Value,NRV)优化问题注意初始化列表什么情况下必须使用初始化列表?注意 默认构造函数 什么情况下编译器需要合成默认构造函数? 一句话:编译器只会去合成non trivial的默认构造函数。 且用户定义的默认构造函数(即使里面什么都没做)为non-trivial!

毛财胜的专栏 1248

C++拷贝构造函数、无名临时对象以及NRV优化分析

那么什么时候编译器会为一个 class 生成一个 default copy constructor 呢? 和 default constructor 一样,C++ Standard 上说,如果 class 没有声明一个 copy constructor,就会有隐含的声明(implicitly declared)或隐含的定义(implicitly defined)出现,和以前一样,C++ Stan

FreeeLinux's blog 1543

《深度探索C++对象模型》阅读笔记(完整版)

《深度探索C++对象模型》完整阅读笔记

2401_82413779的博客 1903

《深度探索C++对象模型》笔记

本文为《深度探索C++对象模型》笔记,简要大纲如下,预计看完需要 10 分钟左右: 1、对象模型 1.1、简单对象模型 1.2、表格驱动数据模型 1.3、c++ 对象模型 1.3.1、类对象中的 string 类型 1.3.2、多态与切割 2、default constructor 2.1、copy constructor 2.1.1、bitwise copy 2.2、NRV - named return value 优化 2.3、initialization list 和 constructor 2.4、

fcsfcsfcs的博客 518

c++对象模型解析(一)

本系列记录在学习过程中,我对于c++对象模型的理解,主要包括如下知识点等底层实现机制。 c++对象大小计算 虚函数(虚表,虚指针) 构造函数,析构函数(定义,作用,什么时候编译器自动生成) 多重继承,虚继承(菱形继承,优劣处) 动态绑定(多态,静态类型与动态类型) struct 和 class之间关系区别 成员变量权限,首先struct中的成员变量默认是私有的,class的成员...

haha与长颈鹿的博客 303

《深度探索C++对象模型》笔记(三)构造函数、拷贝构造和初始化列表

看了这一章后发现:原来最难搞懂的是拷贝构造函数。 2.1默认构造函数.         C++标准是这么说的:对于class X,如果没有任何user-decleared-constructor,那么会有一个default constructor被暗中(implicitly)声明出来。。。。一个被暗中声明出来的default constructor将是一个trival(无用的) const

郭晓东的专栏 3025

C++进阶系列:拷贝构造函数与NRV优化

《深度探索C++对象模型》第二章67页有: 这个程序的第一个版本不能实施NRV优化,因为test class 缺少一个copy constructor. 也就是缺少拷贝构造函数所以不能NRV优化。 NRV优化和拷贝构造函数是有关系的,只有定义了拷贝构造函数才会开启NRV优化,但现代编译器NRV优化的开启一般都与拷贝构造函数没有关系, 早期的 cfront需要一个开关来决定是否应该对代码实行...

uestc_chenmo的博客 1024

NRV优化

一、函数返回局部对象的拷贝的一般实现方式 比如有这么一段函数定义: class X; X bar() { X x1; // 处理 x1.. return x1; } 在学习C++语法时,我们知道了。针对”Xbar()”这样的函数,是返回class X的一个对象的拷贝。其返回值是一个对象,比如叫做x2。在执行return时,x2通过调用拷贝构造函数,拷贝对象x1来实现其

斯瓦馨阁 807
上一篇: 关于虚表的疑惑与证实
下一篇: 字符串分割渣渣版本,适用于任何字节类型字符
早起的虫儿灬
博客等级 码龄10年 54粉丝 114原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值