emplace_back能避免临时对象,因为它直接在vector尾部未构造内存上调用元素类型构造函数;而push_back需先构造临时对象再移动或拷贝。

为什么 emplace_back 能避免临时对象?
因为 emplace_back 直接在 vector 尾部的未构造内存上,用传入的参数调用元素类型的构造函数;而 push_back 先构造一个临时对象(可能触发拷贝或移动),再将其移入或拷贝进容器。
关键区别在于:临时对象是否真实存在过。哪怕类型有移动构造函数,push_back 仍需先完成一次构造(+一次移动),而 emplace_back 是“一步到位”。
- 适用前提是元素类型支持对应参数的构造函数(比如
std::string接受const char*,所以v.emplace_back("hello")合法) - 如果传参不匹配任何构造函数,编译直接失败,不会退化为隐式转换 + 拷贝
- 对 POD 类型(如
int、double)效果一致,但语义更清晰
emplace_back 和 push_back 的参数传递差异
emplace_back 接收的是「构造参数」,不是对象本身;push_back 接收的是「已构造的对象」(或可转为该类型的值)。
例如:
立即学习“C++免费学习笔记(深入)”;
struct Person {
std::string name;
int age;
Person(const std::string& n, int a) : name(n), age(a) {}
};
正确用法:
-
vec.emplace_back("Alice", 30)→ 直接调用Person(const std::string&, int) -
vec.push_back(Person("Alice", 30))→ 先构造临时Person,再移动/拷贝 -
vec.push_back({"Alice", 30})→ 依赖聚合初始化或转换构造函数,行为取决于上下文,不如emplace_back明确
哪些情况用了 emplace_back 也没性能提升?
不是所有场景都能受益。以下情况它和 push_back 实际无差别,甚至更难读:
- 传入已存在的对象:如
v.emplace_back(x)(x是Person类型变量)→ 编译器仍会调用移动构造函数,和v.push_back(std::move(x))等价 - 参数涉及隐式转换链:如
v.emplace_back(42)存入std::string,会调用std::string(size_t),但这不是你本意,且容易误用 - 类型只有默认构造函数,且你没传参:
v.emplace_back()合理,但v.push_back({})或v.push_back(T{})效果相同 - 编译器开启 RVO/NRVO 或移动优化后,
push_back的临时对象开销可能被大幅削减,实测差异趋近于零
调试时容易忽略的陷阱
emplace_back 的完美转发机制会让错误信息变得晦涩,尤其涉及引用、initializer_list 或重载构造函数时。
- 传入右值引用参数(如
std::move(s))后,原对象状态不确定,但emplace_back不会额外增加风险——这点和push_back一样 - 若类构造函数有
std::initializer_list重载,v.emplace_back({1,2,3})可能意外调用它,而非你期望的其他构造函数 - 当 vector 需要扩容,
emplace_back的参数会被再次完美转发到新内存,但如果构造函数抛异常,已构造的元素会被正确析构(强异常安全保证) - 不要为了“看起来更快”而统一替换所有
push_back—— 可读性下降、维护成本上升,且现代编译器对简单类型的优化已经很激进
emplace_back 的典型场景是:明确知道要调用哪个构造函数,且参数是字面量、局部变量或轻量表达式;其余时候,保持原有写法更稳妥。









