Abseil之Return Policy
许多旧的C++代码库显示有点害怕复制对象的模式。幸运的是,由于所谓的“return value optimization”(rvo),我们可以不复制而“复制”。
RVO是几乎所有C++编译器的一个长期特性。考虑下面的C++ 98代码,它有一个复制构造函数和一个赋值操作符。这些功能非常昂贵,开发人员让它们每次使用时都打印一条消息:
class SomeBigObject {public:SomeBigObject() { ... }SomeBigObject(const SomeBigObject& s) {printf("Expensive copy …\n", …);…}SomeBigObject& operator=(const SomeBigObject& s) {printf("Expensive assignment …\n", …);…return *this;}~SomeBigObject() { ... }…
};
请注意,我们有意避免在这里讨论移动操作。更多信息请参见TotW #77
如果这个类有如下工厂方法,您会在恐惧中退缩吗?
static SomeBigObject SomeBigObjectFactory(...) {SomeBigObject local;...return local;
}
看起来效率很低,不是吗?如果我们运行以下程序会发生什么?
SomeBigObject obj = SomeBigObject::SomeBigObjectFactory(...);
简单回答:您可能期望至少创建两个对象:从被调用函数返回的对象和调用函数中的对象。两者都是副本,因此程序打印两条关于昂贵操作的消息。现实世界的答案:没有消息被打印出来——因为从未调用复制构造函数和赋值运算符!
怎么会这样?许多C++程序员编写“高效代码”,创建一个对象并将该对象的地址传递给一个函数,该函数使用该指针或引用对原始对象进行操作。好吧,在下面描述的情况下,编译器可以将这种“低效拷贝”转换为“高效代码”
当编译器在调用函数中看到一个变量(将从返回值构造)和一个被调用函数中的变量(将被返回)时,它意识到不需要这两个变量。在封面下,编译器将调用函数变量的地址传递给被调用函数。
引用C++ 98标准,“每当使用复制构造函数复制临时类对象时,允许实现将原始和副本视为两种不同的引用同一对象的方式,而不执行复制,即使类复制构造函数或析构函数有副作用。对于具有类返回类型的函数,如果返回语句中的表达式是本地对象的名称……允许一个实现省略创建临时对象来保存函数返回值……((第12.8节[class.copy]),C++ 98标准的第15段。C++ 11标准在第12.8节第31段中有相似的语言,但它更复杂)
担心“允许”不是一个很强的承诺?幸运的是,所有现代C++编译器默认情况下都执行RVO,即使在调试版本中,甚至对于非内联函数也是如此。
如何确保编译器执行rvo?
被调用函数应为返回值定义一个变量
SomeBigObject SomeBigObject::SomeBigObjectFactory(...) {SomeBigObject local;…return local;
}
调用函数应将返回值赋给新变量:
// No message about expensive operations:
SomeBigObject obj = SomeBigObject::SomeBigObjectFactory(...);
就这样!
如果调用函数重用现有变量来存储返回值,则编译器无法执行rvo(尽管在这种情况下,移动语义将适用于启用移动的类型):
// RVO won’t happen here; prints message "Expensive assignment ...":
obj = SomeBigObject::SomeBigObjectFactory(s2);
如果被调用函数使用多个变量作为返回值,则编译器也无法执行rvo:
// RVO won’t happen here:
static SomeBigObject NonRvoFactory(...) {SomeBigObject object1, object2;object1.DoSomethingWith(...);object2.DoSomethingWith(...);if (flag) {return object1;} else {return object2;}
}
但是,如果被调用的函数使用一个变量并在多个地方返回它,则可以:
// RVO will happen here:
SomeBigObject local;
if (...) {local.DoSomethingWith(...);return local;
} else {local.DoSomethingWith(...);return local;
}
这可能是你所需要知道的所有关于RVO
还有一件事:Temporaries
RVO使用临时对象,而不仅仅是命名变量。当被调用函数返回临时对象时,可以从rvo中获益:
// RVO works here:
SomeBigObject SomeBigObject::ReturnsTempFactory(...) {return SomeBigObject::SomeBigObjectFactory(...);
}
当调用函数立即使用返回值(存储在临时对象中)时,还可以从rvo中获益
// No message about expensive operations:
EXPECT_EQ(SomeBigObject::SomeBigObjectFactory(...).Name(), s);
最后一个注意事项:如果您的代码需要进行复制,那么就进行复制,不管这些复制是否可以被优化掉。不要为了效率而牺牲正确性。
Abseil之Return Policy相关推荐
- Flurl使用Polly实现重试Policy
❝ 在使用Flurl作为HttpClient向Server请求时,由于网络或者其它一些原因导致请求会有失败的情况,比如HttpStatusCode.NotFound.HttpStatusCode.Se ...
- Policy Iteration与Value Iteration
gym环境:FrozenLake-v0:http://gym.openai.com/envs/FrozenLake-v0/ 代码来自:周博磊老师的GitHub:https://github.com/c ...
- 强化学习(四) - 蒙特卡洛方法(Monte Carlo Methods)及实例
强化学习(四) - 蒙特卡洛方法(Monte Carlo Methods)及实例 4. 蒙特卡洛方法 4.1 蒙特卡洛预测 例4.1:Blackjack(21点) 4.2 动作价值的蒙特卡洛估计 4. ...
- 强化学习(三) - Gym库介绍和使用,Markov决策程序实例,动态规划决策实例
强化学习(三) - Gym库介绍和使用,Markov决策程序实例,动态规划决策实例 1. 引言 在这个部分补充之前马尔科夫决策和动态规划部分的代码.在以后的内容我会把相关代码都附到相关内容的后面.本部 ...
- 深入理解 Java 注解
本文内容基于 JDK8.注解是 JDK5 引入的,后续 JDK 版本扩展了一些内容,本文中没有明确指明版本的注解都是 JDK5 就已经支持的注解. :notebook: 本文已归档到:「blog」 : ...
- linux 进程优先级 之设置实时进程 (另一种方式是设置nice值)【转】
转自:https://www.cnblogs.com/jkred369/p/6731353.html Linux内核的三种调度策略: 1,SCHED_OTHER 分时调度策略, 2,SCHED_FIF ...
- Spring-Retry重试实现原理
点击上方蓝色"程序猿DD",选择"设为星标" 回复"资源"获取独家整理的学习资料! 作者 | Alben 来源 | http://r6d.c ...
- tc-core-library-js学习笔记
1.组件概览 middleware jwtAuthenticator . ./lib/middleware/jwtAuthenticator logger ./lib/middleware/logge ...
- 浅析Linux线程调度
在Linux中,线程是由进程来实现,线程就是轻量级进程( lightweight process ),因此在Linux中,线程的调度是按照进程的调度方式来进行调度的,也就是说线程是调度单元.Linux ...
最新文章
- get_date.sh
- C/C++面试题目集锦
- CCNA笔记-交换机安全
- jquery.form.js插件中ajaxSubmit提交在jquery1.4版本中的应用
- equal_range
- CCF201909-1 小明种苹果
- 最新大润发优鲜小程序逆向分析
- python图片马赛克_Python实现PS滤镜中马赛克效果示例
- python利用集合的无重复性_利用Python程序完成ABAQUS中的一些重复性操作
- java调用数据库存储过程的接口是_JAVA调用数据库存储过程
- 开源项目bootdo的实战开发笔记
- 杭电计算机2010年笔试真题详解
- python逻辑运算符例子_python运算符-实战中常用的三个逻辑运算符使用实例
- English音标(全)与单词家园
- Navicat Premium 历史版本地址
- WPS 手动去除广告
- python编写一个函数判断一个数是否为素数是则返回yes_编写函数,判断一个整数是否为素数,并编写主程序调用该函数。_学小易找答案...
- dodo:人脸识别方法个人见解 (zz)
- HAC集群修改管理员用户密码
- 深度解读BN、LN、WN、CN
热门文章
- 功能丰富的Perl:用Perl读写Excel文件
- java获取反射机制的三种方式
- mysql 查看数据表大小_关于MySQL 查询表数据大小的总结
- .describe() python_Python实现ARMA模型
- 概要设计说明书任务分配
- 【原创】大数据基础之Spark(1)Spark Submit即Spark任务提交过程
- 函数强化练习2(py引入模块、包)
- 在Nginx服务器上用Jenkins发布Vue/React代码的步骤
- 关于浏览器Ajax请求Type为Request Payload而不是Form Data
- 【Linux】Windows Ubuntu 双系统开机选择界面设置