【问题标题】:What is the difference between unordered_map::emplace and unordered_map::insert in C++?C++ 中 unordered_map::emplace 和 unordered_map::insert 有什么区别?
【发布时间】:2014-12-14 07:06:43
【问题描述】:

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

【问题讨论】:

  • 取自reference谨慎使用 emplace 允许构造新元素,同时避免不必要的复制或移动操作。新元素的构造函数(即std::pair<const Key, T>)被调用,参数与提供给emplace的参数完全相同,通过std::forward<Args>(args)...转发。
  • emplace 创建一个新对象,而 insert 采用现有对象。参数不同,因为 emplace 采用构造函数参数。如果您没有要插入的实例,请使用 emplace。

标签: c++ c++11 insert unordered-map emplace


【解决方案1】:

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 并不能保证您会避免复制或移动。此外,采用右值引用的 insert 版本实际上是模板化的,并接受任何类型的 P,因此键值对可以从 P 构造。

Scott Meyers says:

原则上,进位功能有时应该更有效 比他们的插入同行,他们永远不应该少 高效。

编辑: Howard Hinnant 运行 some experiments 显示有时 insertemplace 快)

如果您确实想复制/移动到容器中,使用insert 可能是明智之举,因为如果您传递不正确的参数,则更有可能出现编译错误。您需要更加小心,您将正确的参数传递给 emplacement 函数。

unordered_map::emplace 的大多数实现将导致为新对动态分配内存,即使映射已经包含具有该键的项目并且emplace 将失败。这意味着如果emplace 很有可能会失败,您可能会使用插入获得更好的性能,以避免不必要的动态内存分配。

小例子:

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

Edit2:应要求提供。还可以将unordered_map::emplace 与一个采用多个构造函数参数的键或值一起使用。使用std::pairpiecewise 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
}

【讨论】:

  • “Emplacement 函数通常比插入函数更有效,而且效率永远不会降低。” Howard Hinnant 测量了不同的东西:htmlpreview.github.io/?https://github.com/HowardHinnant/papers/… 另见:@987654327 @
  • @Yakk 啊,对不起。希望这可行:groups.google.com/a/isocpp.org/d/topic/std-discussion/…emplace 用于关联容器通常会从参数创建元素(不仅仅是键),即使它没有被插入。所以如果你已经有一个元素(在关联容器之外)并且想要复制它,insert 可能更有效。
  • 唉,没有人展示过地图的示例,其中 T1 和 T2 的构造函数采用多个参数。我经常使用 map. 之类的东西
  • @ArneVogel 我已经更新了引用以匹配 Effective Modern C++(第 42 页第 301 页)的印刷版中的内容。我认为它是“考虑”而不是“首选”的另一个原因是,当您使用 emplacement 函数时,您必须小心确保传递正确的参数,因为 emplacement 函数可能会执行插入函数会拒绝的转换.
  • @jzions 我添加了一个带有T2 的示例,该示例由多个参数构成。
【解决方案2】:

emplace()insert() 之间的区别已经在Chris Drew's answer 中进行了很好的解释。但是,为了完整起见,我想补充一点,因为C++17 std::unordered_map 提供了两种新的插入方法:try_emplace()insert_or_assign()。让我简单总结一下这些方法:

  • try_emplace()emplace() 的“改进”版本。与emplace() 相比,如果由于unordered_map 中已存在的键而导致插入失败,try_emplace() 不会修改其参数(由于移动操作)。
  • insert_or_assign()operator[] 的“改进”版本。与operator[] 相比,insert_or_assign() 不需要unordered_map 的值类型是默认可构造的。

关于std::maphere的上述新插入方法,我已经写了一个更详细的答案。该答案也适用于std::unordered_map

Simple example code on Coliru

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2010-12-11
    相关资源
    最近更新 更多