繁体   English   中英

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

[英]What is the difference between unordered_map::emplace and unordered_map::insert in C++?

C++ 中的std::unordered_map::emplacestd::unordered_map::insert什么区别?

unordered_map::insert将键值对复制或移动到容器中。 它被重载以接受引用到常量或右值引用

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允许您通过就地构造元素来避免不必要的复制或移动。 它使用完美转发和可变参数模板将参数转发给键值对的构造函数

template<class... Args>
std::pair<iterator,bool> emplace(Args&&... args);

但是这两个功能之间存在大量重叠。 emplace可用于转发到键值对的复制/移动构造函数,这允许它像insert一样使用。 这意味着使用emplace并不能保证您将避免复制或移动。 此外,采用右值引用的insert版本实际上是模板化的,并接受任何类型P以便键值对可以从P构造。

斯科特·迈耶斯 说:

原则上,定位函数有时应该比插入函数更有效,而且它们的效率永远不低。

编辑: Howard Hinnant 进行了一些实验,表明有时insertemplace快)

如果您确实想复制/移动到容器中,那么使用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分段构造函数,您仍然可以避免不必要的复制或移动。

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

emplace()insert()之间的区别已经在Chris Drew's answer 中得到了很好的解释。 但是,为了完整起见,我想补充一点,因为C++17std::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在这里 该答案也适用于std::unordered_map

Coliru 上的简单示例代码

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM