C/C++---emplace和emplace_back
在C++中,emplace
和emplace_back
是容器(如vector
、list
、map
等)提供的用于插入元素的成员函数。它们是C++11引入的特性,主要优势在于能够直接在容器的内存位置上构造对象,而不需要先创建临时对象再将其复制或移动到容器中。
1. emplace_back
适用容器:vector
、deque
、list
等支持尾部插入的容器。
功能:在容器尾部直接构造一个新元素,参数是元素类型的构造函数参数。
示例:
#include <vector>
#include <string>
#include <iostream>struct Person {std::string name;int age;// 构造函数Person(const std::string& n, int a) : name(n), age(a) {}
};int main() {std::vector<Person> people;// 传统方式:先创建临时对象,再移动到容器people.push_back(Person("Alice", 25));// emplace_back 方式:直接在容器内存中构造对象people.emplace_back("Bob", 30); // 直接传递构造函数参数for (const auto& p : people) {std::cout << p.name << ", " << p.age << std::endl;}return 0;
}
输出:
Alice, 25
Bob, 30
优势:
- 效率更高:避免了临时对象的创建和移动操作。
- 语法更简洁:直接传递构造参数,无需显式创建对象。
2. emplace
适用容器:关联容器(如map
、set
)、无序容器(如unordered_map
、unordered_set
)、适配器容器(stack
、queue
、priority_queue
)。
功能:在容器的适当位置直接构造一个新元素,参数是元素类型的构造函数参数。
示例(map
):
std::stack<int> stk;
stk.emplace(42); // 正确
// stk.emplace_back(42); // 错误:stack没有emplace_back
#include <map>
#include <string>
#include <iostream>int main() {std::map<int, std::string> myMap;// 传统方式:先创建临时 pair,再插入myMap.insert(std::make_pair(1, "apple"));// emplace 方式:直接构造 pairmyMap.emplace(2, "banana"); // 直接传递 pair 的构造参数for (const auto& pair : myMap) {std::cout << pair.first << ": " << pair.second << std::endl;}return 0;
}
输出:
1: apple
2: banana
优势:
- 避免临时对象:对于
map
和unordered_map
,无需显式构造std::pair
。 - 自动处理重复键:若键已存在,元素不会插入(与
insert
行为一致)。
3. 区别与注意事项
特性 | emplace_back | emplace |
---|---|---|
适用容器 | 序列容器(如vector 、list ) | 关联容器(如map 、set ) |
插入位置 | 容器尾部 | 自动排序的合适位置(关联容器) |
参数 | 元素类型的构造参数 | 元素类型的构造参数 |
返回值 | void | pair<iterator, bool> (是否插入成功) |
场景 | 推荐方法 | 示例代码 |
---|---|---|
stack/queue 压栈 | emplace | stk.emplace(42); |
vector 尾部插入 | emplace_back | vec.emplace_back(42); |
vector 任意位置插入 | emplace(iterator) | vec.emplace(vec.begin(), 42); |
注意事项:
- 参数匹配:传递的参数必须能被元素类型的构造函数接受。
- 移动语义:若元素类型支持移动构造,
push_back
/insert
可能也很高效,但emplace
仍可能更优。 - 异常安全:若构造函数抛出异常,容器状态保持不变。
总结
- 优先使用
emplace_back
/emplace
:在性能敏感场景或构造参数复杂时。 - 兼容性:若需要兼容旧代码或明确依赖临时对象的创建(如触发特定构造函数),仍可使用
push_back
/insert
。