C++ 中 unordered_map::emplace 和 unordered_map::insert 有什么区别?

2022-01-09 00:00:00 insert c++ c++11 unordered-map emplace

std::unordered_map::emplacestd::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 跑了 一些实验 表明有时 insertemplace 快)

( 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
}

相关文章