你想知道的 std::vector::push_back 和 std::vector::emplace_back
引言
C++ 11 后,标准库容器 std::vector 包含了成员函数 emplace 和 emplace_back。emplace 在容器指定位置插入元素,emplace_back 在容器末尾添加元素。
emplace 和 emplace_back 原理类似,本文仅讨论 push_back 和 emplace_back。
定义
首先看下 Microsoft Docs 对 push_back 和 emplace_back 的定义:
push_back:Adds an element to the end of the vector.emplace_back:Adds an element constructed in place to the end of the vector.
两者的定义我用了加粗字体作区分,那么现在问题就在于什么叫做 constructed in place ?
再来看下官方文档(www.cplusplus.com)怎么介绍 emplace_back 的:
template <class... Args>
void emplace_back (Args&&... args);Inserts a new element at the end of the vector, right after its current last element. This new element is constructed in place using
argsas the arguments for its constructor.
This effectively increases the container size by one, which causes an automatic reallocation of the allocated storage space if -and only if- the new vector size surpasses the current vector capacity.
The element is constructed in-place by callingallocator_traits::constructwithargsforwarded.
A similar member function exists,push_back, which either copies or moves an existing object into the container.
简而言之,push_back 会构造一个临时对象,这个临时对象会被拷贝或者移入到容器中,然而 emplace_back 会直接根据传入的参数在容器的适当位置进行构造而避免拷贝或者移动。
为什么我们有了 emplace_back 还需要 push_back?
这部分内容进一步对如何区分 push_back 和 emplace_back 做了解答。
Stack Overflow 有一项回答我认为已经解释的较为清楚,因此这里部分转译过来。
翻译带有个人理解,非直译,原文参考:https://stackoverflow.com/questions/10890653/why-would-i-ever-use-push-back-instead-of-emplace-back
以下为译文:
关于这个问题我在过去 4 年思考良多,我敢说大多数关于 push_back 和 emplace_back 的解释都不够完善。
去年,我在一次关于 C++ 的介绍中(链接参考原文)讨论了 push_back 和 emplace_back 的相关议题,这两者最主要的区别来自于:是使用隐式构造函数还是显示构造函数(implicit vs. explicit constructors)。
先看下面的示例:
std::vector<T> v;
v.push_back(x);
v.emplace_back(x);
传统观点认为 push_back 会构造一个临时对象,这个临时对象会被移入到 v 中,然而 emplace_back 会直接根据传入的参数在适当位置进行构造而避免拷贝或者移动。从标准库代码的实现角度来说这是对的,但是对于提供了优化的编译器来讲,上面示例中最后两行表达式生成的代码其实没有区别。
真正的区别在于,emplace_back 更加强大,它可以调用任何类型的(只要存在)构造函数。而 push_back 会更加严谨,它只调用隐式构造函数。隐式构造函数被认为是安全的。如果能够通过对象 T 隐式构造对象 U,就认为 U 能够完整包含 T 的所有内容,这样将 T 传递给 U 通常是安全的。正确使用隐式构造的例子是用 std::uint32_t 对象构造 std::uint64_t 对象,错误使用隐式构造的例子是用 double 构造 std::uint8_t。
我们必须在编码时小心翼翼。我们不想使用强大/高级的功能,因为它越是强大,就越有可能发生意想不到的错误。如果想要调用显示构造函数,那么就调用 emplace_back。如果只希望调用隐式构造函数,那么请使用更加安全的 push_back。
再看个示例:
std::vector<std::unique_ptr<T>> v;
T a;
v.emplace_back(std::addressof(a)); // compiles
v.push_back(std::addressof(a)); // fails to compile
std::unique_ptr<T> 包含了显示构造函数通过 T* 进行构造。因为 emplace_back 能够调用显示构造函数,所以传递一个裸指针并不会产生编译错误。然而,当 v 超出了作用域,std::unique_ptr<T> 的析构函数会尝试 delete 类型 T* 的指针,而类型 T* 的指针并不是通过 new 来分配的,因为它保存的是栈对象的地址,因此 delete 行为是未定义的。
这不是为了示例而特意写的代码,而是一个我遇到的实际问题。原本 v 是 std::vector<T *> 类型,迁移到 C++ 11 后,我修改为 std::vector<std::unique_ptr<T>>。并且,那时我错误地认为 emplace_back 能够做 push_back 所能做的所有事情,因此将 push_back 也改为了 emplace_back。
如果我保留使用更加安全的 push_back,那么我会立马发现这个 bug。不幸的是,我意外地隐藏了这个 bug 并直到几个月后才重新发现它。
引用
- https://docs.microsoft.com/en-us/cpp/standard-library/vector-class?view=msvc-160
- https://www.cplusplus.com/reference/vector/vector/emplace_back/
- https://stackoverflow.com/questions/10890653/why-would-i-ever-use-push-back-instead-of-emplace-back
你想知道的 std::vector::push_back 和 std::vector::emplace_back的更多相关文章
- 你想知道的3D Touch开发全在这里了
前言 iPhone 6s和iPhone 6s Plus为多点触摸界面带来了强大的3D触摸新维度.这项新技术可以感知用户按下显示屏的深度,让他们比以往任何时候都更能使用你的应用程序和游戏.更多关于3D ...
- [翻译]你不会想知道的kobject,kset,和ktypes
---------------------------------------------------------------------------------------------------- ...
- CLR中你想知道的事
CLR是什么? CLR 公共语言运行时,是一个可由多个语言共同使用的运行环境,核心(内存管理,程序集加载,安全性,异常处理和多线程) Visual Studio是一种编译器,编译器也可称为语法检查器和 ...
- All in One 你想知道的 hacker 技术都在这里
作者:HelloGitHub-小鱼干 hacker 这个词,大多数理解为黑客,而维基百科对其的定义为--黑客(Hacker)是指对设计.編程和计算机科学方面具高度理解的人,在本文中 hacker 主要 ...
- 关于WPF你应该知道的2000件事
原文 关于WPF你应该知道的2000件事 以下列出了迄今为止为WPF博客所知的2,000件事所创建的所有帖子. 帖子总数= 1,201 动画 #7 - 基于属性的动画 #686 - 使用动画制作图像脉 ...
- 【转载】在IT界取得成功应该知道的10件事
在IT界取得成功应该知道的10件事 2011-08-11 13:31:30 分类: 项目管理 导读:前面大多数文章都是Jack Wallen写的,这是他的新作,看来要成为NB程序员还要不停的自我总结 ...
- 理工科应该的知道的C/C++数学计算库(转)
理工科应该的知道的C/C++数学计算库(转) 作为理工科学生,想必有限元分析.数值计算.三维建模.信号处理.性能分析.仿真分析...这些或多或少与我们常用的软件息息相关,假如有一天你只需要这些大型软件 ...
- Git / 程序员需要知道的12个Git高级命令
众所周知,Git目前已经是分布式版本控制领域的翘楚,围绕着Git形成了完整的生态圈.学习Git,首先当然是学习Git的基本工作流.相比于SVN等传统版本控制系统来说,Git是专为分布式版本控制而生的强 ...
- 关于Solr搜索标点与符号的中文分词你必须知道的(mmseg源码改造)
关于Solr搜索标点与符号的中文分词你必须知道的(mmseg源码改造) 摘要:在中文搜索中的标点.符号往往也是有语义的,比如我们要搜索“C++”或是“C#”,我们不希望搜索出来的全是“C”吧?那样对程 ...
随机推荐
- Python 是什么语言
Python 是 解释型语言,强类型定义语言,动态类型定义语言 编译型语言 & 解释型语言 编译型语言:代码在执行前,需要编译(成机器语言文件,如 .exe 文件):以后再运行时,直接使用编译 ...
- 使用opencv-python实现MATLAB的fspecial('Gaussian', [r, c], sigma)
reference_opencv实现高斯核 reference_MATLAB_fspecial函数说明 # MATLAB H = fspecial('Gaussian', [r, c], sigma) ...
- Graphviz - Graph Visualization Software 开源可视化绘图工具(visio 类)
http://www.graphviz.org/Download_windows.php Welcome to Graphviz Available translations: Romanian, ...
- Kotlin 入门教程
Kotlin 入门教程 Android / Java https://developer.android.com/kotlin?hl=zh-cn 使用 Kotlin 开发 Android 应用 使用 ...
- NGK英国路演圆满结束,未来科技布局看好NGK公链技术
近日,NGK全球路演英国站在首都伦敦圆满结束.区块链业内专家.各投行精英.各市场节点代表.八大产业代表参加了此次路演.同时,英国经济学人.每日邮报.金融时报等近百家财经媒体对此路演进行了大力报道.并且 ...
- alpakka-kafka(1)-producer
alpakka项目是一个基于akka-streams流处理编程工具的scala/java开源项目,通过提供connector连接各种数据源并在akka-streams里进行数据处理.alpakka-k ...
- python 相对路径和绝对路径的区别
一,Python中获得当前目录和上级目录 获取当前文件的路径: from os import path d = path.dirname(__file__) #返回当前文件所在的目录 # __file ...
- ubuntu无法连接有线网
问题描述: ubuntu下仅能连接无线网,不能连接有线网,在有线网的下面是没有选项可供连接. 解决方法: 编辑 /etc/network/interfaces 这个文件 将里面仅仅写两句话 auto ...
- .net core 和 WPF 开发升讯威在线客服系统:怎样实现拔网线也不丢消息的高可靠通信(附视频)
本系列文章详细介绍使用 .net core 和 WPF 开发 升讯威在线客服与营销系统 的过程.本产品已经成熟稳定并投入商用. 在线演示环境:https://kf.shengxunwei.com 注意 ...
- 14_MySQL条件查询
本节所涉及的sql语句: -- 去除结果集中的重复记录 SELECT job FROM t_emp; SELECT DISTINCT job FROM t_emp; SELECT DISTINCT j ...