C++ 中 unordered_map::emplace 和 unordered_map::insert 有什么区别?
std::unordered_map::emplace
和std::unordered_map::insert
在C++中有什么区别?
What is the difference between std::unordered_map::emplace
and std::unordered_map::insert
in C++?
推荐答案
unordered_map::insert
将键值对复制或移动到容器中.它被重载以接受对常量的引用或右值引用:
unordered_map::insert
copies or moves a key-value pair into the container. 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
允许您通过就地构造元素来避免不必要的复制或移动.它使用完美转发和可变参数模板将参数转发给键值的构造函数对:
unordered_map::emplace
allows you to avoid unnecessary copies or moves by constructing the element in place. It uses perfect forwarding and a variadic template to forward arguments to the constructor of the key-value pair:
template<class... Args>
std::pair<iterator,bool> emplace(Args&&... args);
但是这两个功能之间有很多重叠之处.emplace
可用于转发到键值对的复制/移动构造函数,这允许它像 insert
一样使用.这意味着使用 emplace
并不能保证您会避免复制或移动.此外,采用右值引用的 insert
版本实际上是模板化的,并接受任何类型的 P
以便键值对可以从 P
.
But there is a great deal of overlap between the two functions. emplace
can be used to forward to the copy/move constructor of the key-value pair which allows it to be used just as insert
would. This means that use of emplace
doesn't guarantee you will avoid copies or moves. Also the version of insert
that takes an rvalue-reference is actually templated and accepts any type P
such that the key-value pair is constructible from P
.
Scott Meyers 说:
原则上,进位功能有时应该更有效比他们的插入同行,他们永远不应该少高效.
In principle, emplacement functions should sometimes be more efficient than their insertion counterparts, and they should never be less efficient.
( Howard Hinnant 跑了 一些实验 表明有时 insert
比 emplace
快)
( Howard Hinnant ran some experiments that showed sometimes insert
is faster than emplace
)
如果您确实想要复制/移动到容器中,使用 insert
可能是明智的,因为如果您传递不正确的参数,则更有可能出现编译错误.您需要更加小心,您将正确的参数传递给 emplacement 函数.
If you definitely do want to copy/move into the container it might be wise to use insert
because you are more likely to get a compilation error if you pass incorrect arguments. You need to be more careful you are passing the correct arguments to the emplacement functions.
unordered_map::emplace
的大多数实现将导致为新对动态分配内存,即使地图已经包含具有该键的项目并且 emplace
将失败.这意味着如果 emplace
很有可能会失败,您可能会使用 insert 获得更好的性能,以避免不必要的动态内存分配.
Most implementations of unordered_map::emplace
will cause memory to be dynamically allocated for the new pair even if the map contains an item with that key already and the emplace
will fail. This means that if there is a good chance that an emplace
will fail you may get better performance using insert to avoid unneccessary dynamic memory allocations.
小例子:
#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 << "
";
}
Edit2:应要求提供.也可以将 unordered_map::emplace
与采用多个构造函数参数的键或值一起使用.使用 std::pair
分段构造函数您仍然可以避免不必要的复制或移动.
On request. It is also possible to use unordered_map::emplace
with a key or value that takes more than one constructor parameter. Using the std::pair
piecewise constructor you can still avoid unnecessary copies or moves.
#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
}
相关文章