目录
[1. 核心设计逻辑差异](#1. 核心设计逻辑差异)
[2. 参数传递与类型推导](#2. 参数传递与类型推导)
[3. 性能对比与适用场景](#3. 性能对比与适用场景)
[4. 使用建议](#4. 使用建议)
在C++标准库中,insert()和emplace()是容器(如vector、list、map、set等)中用于添加元素的成员函数,两者的核心区别在于对象构造机制 和性能特性。
1. 核心设计逻辑差异
-
insert()直接将已存在的对象 (通过拷贝或移动)插入容器。需要用户提前构造好对象,再传递给容器。例如:(说人话:需要被插入的那个玩意儿是存在、已经被定义好了的,通过复制粘贴完成操作)
-
emplace()利用完美转发 和原地构造 技术,直接在容器的内存中构造对象,避免额外拷贝或移动。它接受构造对象所需的参数(而非对象本身),由容器内部调用元素的构造函数。例如:
cpp//这是insert() map< int, string > m; pair<int, string> a=(1, "hello"); m.insert(a); // 1,先复制a, 2,再粘贴a //这是empalce() map< int, string > m; m.emplace(1, "hello"); // 它懒得去复制, 直接摇人,摇map里的构造函数自己去原地造一个要插入的那玩意出来
2. 参数传递与类型推导
-
insert()参数通常接受以下形式:
- 显式对象:
T.insert(obj) - 迭代器位置 + 对象:
T.insert(it, obj) - 初始化列表:
T.insert({obj1, obj2, ...})
对于关联容器(如
map),若插入的键已存在,insert会失败(返回插入结果),但不会修改已有元素。 - 显式对象:
-
emplace()参数接受构造对象所需的任意参数组合(通过模板参数推导),支持隐式转换和完美转发。例如
cpp
struct Point {
Point(int x, int y) {}
};
vector<Point> v;
v.emplace(1, 2); // 直接传递构造参数,无需手动写Point对象
3. 性能对比与适用场景
- 性能优势场景
- 大对象或移动成本高 :
emplace避免临时对象的构造和拷贝/移动开销。例如,向vector<array<int, 1000>>插入元素时,emplace直接在容器内存中构造数组,而insert需先构造临时数组再移动。 - 仅需默认构造 :若容器元素支持默认构造(如
std::list<T>),emplace可直接在容器中创建默认对象,再修改其值,比insert更高效。 - 关联容器中的复杂对象 :如
map的value_type是pair<const Key, Value>,emplace可避免手动构造pair的冗余代码。
- 大对象或移动成本高 :
- 性能无差异场景
- 较小基本的数据类型:拷贝/移动成本低,两者性能接近。
- 编译器优化:现代编译器可能对
insert进行优化(如返回值优化RVO),缩小与emplace的差距。
- 潜在风险
-
自插入 :
emplace在容器内直接构造对象,若参数引用了容器内已有元素(如vec.emplace(vec[0])),可能导致未定义行为;insert通常更安全(因参数是独立对象)。 -
类型推导错误 :
emplace的参数若能匹配多种构造函数,可能导致编译错误或意外行为。例如:cppA { A(int) {} A(double) {} }; vector<A> v; v.emplace(1); // 调用A::A(int) v.emplace(1.0); // 调用A::A(double)
-
4. 使用建议
- 优先使用
emplace的场景- 插入大对象 或移动成本高 的对象(如
vector、string)。 - 插入关联容器 (如
map、set)中的复杂类型(如pair、自定义结构体)。 - 代码简洁性优先:减少手动构造临时对象的冗余代码。
- 插入大对象 或移动成本高 的对象(如
- 优先使用
insert的场景- 插入已存在的对象(如从其他容器转移数据)。
- 需明确避免自插入风险(如参数可能引用容器内元素)。
- 兼容旧版C++(C++11之前无
emplace)。
- 通用准则
- 性能敏感场景 :通过性能测试(如
vector插入大量对象)验证emplace的优势。 - 代码可读性 :在参数与构造函数清晰匹配时,
emplace可提升可读性(如map插入pair)。 - 异常安全 :
emplace在构造对象时若抛出异常,容器不会添加元素;insert在拷贝/移动过程中抛出异常时,行为类似(需确保对象支持异常安全的拷贝/移动)。
- 性能敏感场景 :通过性能测试(如
总结
| 特性 | insert |
emplace |
|---|---|---|
| 对象构造 | 拷贝/移动已有对象 | 直接在容器内存中构造对象 |
| 参数形式 | 对象或迭代器+对象 | 构造对象所需的参数(完美转发) |
| 性能 | 可能引入额外拷贝/移动开销 | 避免临时对象,通常更高效 |
| 适用容器 | 所有容器 | C++11起支持所有容器 |
| 自插入风险 | 较低(参数独立) | 较高(参数可能引用容器内元素) |
推荐实践:
在C++11及以上工作环境中,默认使用emplace以获得性能和代码简洁性优势;在需要明确避免自插入或兼容旧代码时,选择insert。始终通过实际测试验证性能差异,并关注代码可读性和异常安全性。
在C++11及以上竞赛环境中,需要根据复制/移动成本判断情况,移动成本可以从下面两个方面判断:
1,需要被插入的对象是否已经存在了?
2,需要被插入的是大对象( vector、string)还是小对象( int、char、double、bool......)
如果需要插入的对象已经存在了,且是小对象,那肯定是用insert()
如果****需要插入的对象还不存在,或者是大对象,移动成本高,那还是用emplace()吧
如果还是无法判断,那就直接****用emplace()吧。(要是问我理由,你猜为什么emplace是更新到C11了才有的)