这个问题在面试、代码评审、技术群里都出现过太多次。甚至已经演化成了一种经验法则:能用 emplace_back 就别用 push_back。
但如果你真的把两者的实现、调用场景、以及标准的约束拆开来看,会发现事情远没有这么简单——有些情况下 emplace_back 更快,有些情况下它不但不快,反而更容易写出隐蔽 Bug。
这篇文章不站队,也不喊口号,只把这件事说清楚。
先把话说在前面
push_back 和 emplace_back 的核心区别只有一句话:
push_back 接受一个“已经构造好的对象”, emplace_back 接受“构造这个对象所需的参数”。
听起来很抽象,我们直接看代码。
最经典的对比场景
std::vector<std::string> v;
v.push_back(std::string("hello"));
v.emplace_back("hello");
这段代码你一定见过,也一定有人告诉你:下面那行更快。
这次他们没骗你。
发生了什么?
push_back
- 构造一个临时的
std::string("hello")
- 把这个临时对象 move / copy 进 vector
- 销毁临时对象
emplace_back
- 在 vector 内部直接构造
std::string("hello")
少了一次对象构造,少了一次 move。
在这个场景下,emplace_back 的确更高效,而且语义也更清晰。
那是不是就该“无脑 emplace_back”?
如果事情这么简单,这个问题也就不会反复被讨论了。
来看另一个很常见的写法。
std::string s = "hello";
v.push_back(s);
v.emplace_back(s);
你觉得这两行有什么区别?
答案是:几乎没有。
原因很简单
push_back(s):拷贝 s
emplace_back(s):用 s 调用 std::string 的拷贝构造函数
最终调用的是同一个构造函数。
这里没有“原地构造带来的额外收益”,也没有减少任何一次拷贝。如果你指望 emplace_back 在这种情况下更快,那只是心理安慰。
emplace_back 不是“更快版本的 push_back”
这是很多人理解偏差的根源。
标准并没有说:
emplace_back 在任何情况下都比 push_back 高效
它只保证了一件事:
emplace_back 会在容器内部直接构造元素。
至于构造的过程本身是否更快,完全取决于你传了什么参数。
一个容易被忽略的“反例”
来看这样一段代码:
std::vector<std::pair<int, int>> v;
v.push_back({1, 2});
v.emplace_back({1, 2});
直觉上你可能觉得:
emplace_back 还是更优吧?
但实际上,第二行甚至是有问题的写法。
为什么?
emplace_back 接收的是“构造参数”,而不是一个已经构造好的 std::pair<int, int>。
{1, 2} 在这里并不是参数包,而是一个 initializer_list 风格的临时对象表达式。
不同编译器、不同标准版本下,这段代码的行为并不一致,甚至可能直接编译失败。
正确、清晰、可预期的写法反而是:
v.emplace_back(1, 2);
这也是 emplace_back 的真正优势场景:参数能一一对应构造函数参数。
emplace_back 更容易写危险代码
再看一个实际项目里很容易踩的坑。
std::vector<std::string> v;
v.reserve(1);
std::string s = "hello";
v.emplace_back(std::move(s));
很多人以为这和:
v.push_back(std::move(s));
是等价的。
但如果你稍微不小心,把代码写成了:
v.emplace_back(s.c_str());
问题就来了。
发生了什么?
s.c_str() 返回的是一个 const char*
emplace_back 会尝试用它构造 std::string
- 如果 vector 发生扩容、异常、或构造顺序变化
你就可能在完全没意识到的情况下,引入悬垂指针相关的未定义行为。
而 push_back(std::string(s.c_str())) 至少在语义上更明确:先构造,再入容器。
emplace_back 的“灵活”,某种程度上也是“更容易犯错”。
标准库实现者是怎么用的?
翻一翻 libc++ / libstdc++ 的源码你会发现:
- 简单、明确的对象入容器 → 用
push_back
- 需要完美转发参数 → 用
emplace_back
它们并没有“全盘替换”。
这本身就说明了一件事:emplace_back 是补充,不是替代。
那到底该怎么选?
如果只记一句话:
当你本来就要构造一个临时对象时,用 push_back; 当你手里只有构造参数时,用 emplace_back。
再具体一点:
适合 emplace_back(args...) 的情况:
- 构造参数清晰
- 能减少一次临时对象构造
- 类型构造成本高(如 string、vector、复杂对象)
适合 push_back(obj) 的情况:
- 已经有现成对象
- 语义更直观
- 可读性优先于“理论上的零开销”
核心建议:
- 不是为了“看起来更高级”滥用 emplace_back
- 不要把 emplace_back 当成性能万能药
最后一点真实感受
很多性能问题,从来不是出在 push_back 还是 emplace_back 上。 而是:
- 容器是否提前
reserve
- 对象设计是否合理
- 拷贝 / move 是否被意外禁用
- 生命周期是否清晰
emplace_back 解决的是“多构造一次”的问题, 但它解决不了“你本来就不该这么写”的问题。
如果你在 code review 里看到有人无脑把 push_back 全部替换成 emplace_back, 那大概率不是性能意识强,而是理解还停留在表层。
这一点,标准库从来没替我们做过保证。深入理解 STL容器 的内部机制,才能做出更明智的选择。希望这篇文章能帮你理清思路,更多C++进阶讨论,欢迎到 云栈社区 交流分享。