emplace在参数不匹配时直接编译失败,而push可能隐式转换后构造临时对象再移动,掩盖类型问题;emplace就地构造无中间对象,对高成本或不可移动类型更高效、更安全。

emplace 在构造函数参数不匹配时会直接失败
当 std::vector 的元素类型没有接受传入参数的构造函数时,emplace 编译失败,而 push 可能意外通过——因为 push 先构造临时对象,再移动/拷贝。这看似“更宽容”,实则是隐藏了类型不匹配问题。
-
emplace是“就地构造”:它把参数原样转发给元素类型的构造函数,不经过任何中间对象 -
push总是需要一个可构造的右值(或 const 引用),哪怕你传的是int,它也要先造个T(int)临时量,再 move/copy 进容器 - 比如
vector,v.emplace(v.end(), "hello")调用string(const char*);而v.push_back("hello")先构造临时string,再 move —— 多一次 move 构造调用
push_back(T{}) 和 emplace_back() 的行为差异
空初始化场景下,两者的语义和开销不同:前者一定触发一次默认构造 + 一次移动(即使编译器优化掉,语义上仍存在),后者直接在 vector 内存上默认构造,无中间对象。
-
v.push_back(T{}):先在栈上构造临时T{},再 move 到 vector 分配的内存里 -
v.emplace_back():直接在 vector 底层内存上调用T::T(),零额外对象 - 若
T移动代价高(如含大缓冲区的类),或禁止移动(delete了移动构造),push_back(T{})可能编译失败,而emplace_back()依然有效
转发失败导致 emplace 编译错误的典型情况
参数类型不能隐式转换、或完美转发被模板推导干扰时,emplace 会报错,而 push 因为走的是常规构造路径,反而“糊弄过去”。这不是 emplace 的缺陷,而是它更严格地暴露了接口契约问题。
- 例如:自定义类
Widget只有explicit Widget(int),那么v.emplace_back(42)合法,但v.emplace_back(42.0)编译失败(无法隐式转 int) - 而
v.push_back(42.0)却可能通过:先用double→int隐式转换构造临时Widget,再 move —— 但这是静默截断,容易出 bug - 再如:带右值引用参数的构造函数,若传左值且未加
std::move,emplace转发失败,push却可能调用拷贝构造“蒙混过关”
性能差异只在构造开销大或不可移动类型上才明显
对 int、std::string_view 这类 trivial 或廉价类型,emplace 和 push 生成的汇编几乎一样,别为这点差异改代码。真正值得换的,是那些构造成本高、或禁用拷贝/移动的类型。
立即学习“C++免费学习笔记(深入)”;
- 常见高成本场景:含堆分配的容器(
std::vector)、带复杂初始化逻辑的类、std::unique_ptr等资源持有者 - 不可移动类型:如
std::array<:mutex>——push_back必编译失败(没 move 构造),emplace_back却可以就地构造 - 注意:如果元素类型有 noexcept 的移动构造,编译器对
push_back的优化(如 elision)可能抹平差距;但emplace的语义更确定、更少依赖优化
临时对象优化不是靠编译器猜,而是靠你选对接口。emplace 把控制权交还给类型本身,不引入任何多余生命周期;一旦你看到 push_back 配合临时对象写法(比如 push_back(Foo(a,b))),基本就是可以替换成 emplace_back(a,b) 的信号 —— 除非你真需要那个临时对象做别的事。








