为何std::push_back()表现类似std::emplace_back()?构造调用疑问
关于std::vector push_back的构造调用疑问解答
问题场景
在未调用reserve()的std::vector中执行push_back(std::move(three))时,输出先出现“move constructed”,随后又出现“copy constructed”。原本认为push_back会先在容器外构造对象再拷贝至容器,而emplace_back直接在容器内完成对象创建、拷贝或移动,但实际表现和认知不符,存在两个疑问:
- 最后一次
push_back为何先调用移动构造函数再调用拷贝构造函数? - 为何
push_back的表现与emplace_back相似?
疑问解答
1. 先移动再拷贝的原因
这是因为vector扩容触发的元素转移导致的:
- 第一步,
push_back(std::move(three))会直接把three移动到vector当前的末尾位置(此时这个位置还在旧的内存缓冲区里),所以触发一次移动构造。 - 第二步,vector检查到当前容量已经耗尽,必须扩容。此时需要把旧内存里的所有元素(包括刚移动进来的那个)转移到新分配的更大内存中。如果你的元素类型的移动构造函数没有标记为
*noexcept*,vector为了保证异常安全,会选择用拷贝构造来转移元素——因为如果移动过程中抛出异常,旧元素可能已经被破坏,无法回滚;但拷贝的话,旧元素还保持完好,能安全恢复操作。
简单总结:先移动构造新元素到旧内存,扩容时因移动构造不保证异常安全,只能拷贝所有元素到新内存,所以出现两次构造调用。
2. push_back和emplace_back表现相似的原因
你的旧认知存在偏差:
push_back并非总是先在容器外构造再拷贝。当传入的是右值(比如std::move(three)),push_back会直接在容器的内存空间里移动构造元素,而非先构造再拷贝。emplace_back的核心是通过完美转发参数,直接在容器内存里构造元素;而push_back对右值的处理,本质也是利用完美转发,直接在容器内完成构造(移动或拷贝)。两者在处理右值时的底层逻辑高度一致,所以表现会相似。
举个直观的例子:push_back(std::move(obj))和emplace_back(std::move(obj))的效果几乎完全相同,都是直接在容器内存里移动构造元素;只有当传入左值时,两者才会一致地触发拷贝构造(除非手动将左值转为右值)。
内容的提问来源于stack exchange,提问作者Liviu Dorobantu
相关产品推荐
相关产品推荐

