




push_back 传左值时调用一次拷贝或移动构造,传右值时优先移动构造;emplace_back 直接就地构造,避免中间对象,仅当参数匹配目标构造函数时才真正减少构造次数。
当传入一个已存在的对象时,push_back 一定调用一次拷贝构造(或移动构造,如果对象支持且满足条件);如果传入的是临时对象或右值,编译器可能优化掉拷贝(RVO/NRVO),但语义上仍要求类型具备可拷贝/可移动性。
常见错误现象:std::vector 编译失败——因为 NonCopyable 禁用了拷贝和移动,而 push_back 的重载签名强制要求可移动(C++11 起)。
std::move 显式转换)emplace_back 直接在 vector 尾部的未初始化内存上调用类的构造函数,绕过中间对象的创建。它接受任意参数列表,完美转发给目标类型的构造函数。
使用场景:构造开销大、不可拷贝/不可移动、或带多个参数的类型(如 std::string("hello")、std::pair)。
std::vector<:string> 执行 v.emplace_back("hello"):只调用一次 std::string 的 const char* 构造函数v.push_back(std::string("hello")):先构造临时 std::string,再移动(或拷贝)进 vector,至少两次构造行为emplace_back 会编译失败,而非静默降级不是所有情况都适合无脑换用 emplace_back。它的优势依赖于“就地构造”能省掉的步骤是否真实存在。
性能陷阱:
int、std::array),两者生成的汇编几乎一致,无实际差异v.emplace_back(get_string(), get_int())),这些临时对象仍会先被构造,再转发——只是 vector 内部不额外拷贝emplace_back 的模板展开可能拖慢编译速度,且错误信息更难读(尤其参数类型不匹配时)push_back 做了移动优化,实际观测不到差别最直接的方式是写一个带计数器的测试类,重载各构造函数并打印日志:
struct Tracked {
static int ctor_count, copy_count, move_count;
Tracked() { ++ctor_count; }
Tracked(int) { ++ctor_count; }
Tracked(const Tracked&) { ++copy_count; }
Tracked(Tracked&&) { ++move_count; }
};
int Tracked::ctor_count = 0, Tracked::copy_count = 0, Tracked::move_count = 0;
然后分别运行:
v.push_back(Tracked{42}); → 观察 ctor_count 和 move_count
v.emplace_back(42); → 通常只有 ctor_count +1v.push_back(Tracked{}); → 若 Tracked{} 是左值,还会触发一次移动或拷贝注意:编译器优化(-O2)可能内联或消除部分调用,建议关掉优化或加 volatile 强制保留日志逻辑来观察真

真正容易被忽略的是:emplace_back 的“零拷贝”优势只在构造函数参数能直接传递、且目标类型确实支持对应构造签名时才成立。传错参数类型不会报“构造失败”,而是报一长串模板推导错误——这时候回头检查构造函数声明比硬调参数更省时间。