C ++中std::unordered_map::emplace
和std::unordered_map::insert
之间有什么区别?
答案 0 :(得分:38)
unordered_map::insert
将键值对复制或移动到容器中。 It is overloaded to accept reference-to-const or an rvalue reference:
std::pair<iterator,bool> insert(const std::pair<const Key, T>& value);
template<class P>
std::pair<iterator,bool> insert(P&& value);
unordered_map::emplace
允许您通过构建元素来避免不必要的副本或移动。它使用完美转发和可变参数模板forward arguments to the constructor of the key-value pair:
template<class... Args>
std::pair<iterator,bool> emplace(Args&&... args);
但这两个功能之间存在很多重叠。 emplace
可用于转发到键值对的复制/移动构造函数,这允许它像insert
那样使用。这意味着使用emplace
并不能保证您避免复制或移动。另外,采用rvalue-reference的insert
版本实际上是模板化的,并接受任何类型P
,以便键值对可以从P
构造。
原则上,进驻功能有时应该更有效率 而不是他们的插入对应物,他们应该永远不会少 高效。
(编辑> Howard Hinnant跑了some experiments,有时显示insert
比emplace
更快
如果您确实想要复制/移入容器,那么使用insert
可能是明智的,因为如果传递错误的参数,则更有可能出现编译错误。您需要更加小心,将正确的参数传递给安置函数。
unordered_map::emplace
的大多数实现将导致为新对动态分配内存,即使映射包含已具有该键的项目且emplace
将失败。这意味着如果emplace
很可能会失败,那么使用insert可以获得更好的性能,以避免不必要的动态内存分配。
小例子:
#include <unordered_map>
#include <iostream>
int main() {
auto employee1 = std::pair<int, std::string>{1, "John Smith"};
auto employees = std::unordered_map<int, std::string>{};
employees.insert(employee1); // copy insertion
employees.insert(std::make_pair(2, "Mary Jones")); // move insertion
employees.emplace(3, "James Brown"); // construct in-place
for (const auto& employee : employees)
std::cout << employee.first << ": " << employee.second << "\n";
}
编辑2:根据要求。也可以将unordered_map::emplace
与带有多个构造函数参数的键或值一起使用。使用std::pair
piecewise constructor,您仍然可以避免不必要的副本或移动。
#include <unordered_map>
#include <iostream>
struct Employee {
std::string firstname;
std::string lastname;
Employee(const std::string& firstname, const std::string& lastname)
: firstname(firstname), lastname(lastname){}
};
int main() {
auto employees = std::unordered_map<int, Employee>{};
auto employee1 = std::pair<int, Employee>{1, Employee{"John", "Smith"}};
employees.insert(employee1); // copy insertion
employees.insert(std::make_pair(2, Employee{"Mary", "Jones"})); // move insertion
employees.emplace(3, Employee("Sam", "Thomas")); // emplace with pre-constructed Employee
employees.emplace(std::piecewise_construct,
std::forward_as_tuple(4),
std::forward_as_tuple("James", "Brown")); // construct in-place
}
答案 1 :(得分:1)
emplace()
中已经很好地解释了insert()
和Chris Drew's answer之间的区别。但是,出于完整性考虑,我想补充一点,因为C++17 std::unordered_map
提供了两种新的插入方法:try_emplace()
和insert_or_assign()
。让我简要总结一下这些方法:
try_emplace()
是emplace()
的“改进”版本。与emplace()
相比,如果try_emplace()
中已经存在键导致插入失败,unordered_map
不会修改其参数(由于移动操作)。insert_or_assign()
是operator[]
的“改进”版本。与operator[]
相比,insert_or_assign()
不需要unordered_map
的值类型是默认可构造的。我针对上述std::map
here的新插入方法写了更详细的答案。该答案也适用于std::unordered_map
。