std::unordered_map::emplace 对象创建

vmp*_*str 6 c++ unordered-map c++11 emplace

我正在选择将事物放入 unordered_map 的两种方法之一:

std::unordered_map<Key, Value> map;
map.emplace(
  std::piecewise_construct,
  std::forward_as_tuple(a),
  std::forward_as_tuple(b, c, d));
Run Code Online (Sandbox Code Playgroud)

对比

std::unordered_map<Key, DifferentValue> map;
auto& value = map[a];
if (value.isDefaultInitialized())
  value = DifferentValue(b, c, d);
Run Code Online (Sandbox Code Playgroud)

我做了一些实验,看看哪个效果更好,发现在插入唯一元素时,行为(如效率)基本上是等效的。

然而,在插入重复项的情况下,考虑到Value或DifferentValue的构造不是微不足道的,我惊讶地发现,emplace构造对象而不管它是否插入它。

因此,在这种情况下,第二种方法似乎胜出,因为默认构造函数中只有 isDefaultInitialized_(true) 而不是更多。

对于 emplace,代码似乎是:

... _M_emplace(std::true_type, _Args&&... __args) {
  __node_type* __node = _M_allocate_node(std::forward<_Args>(__args)...);
  const key_type& __k = this->_M_extract()(__node->_M_v);
  ...
  if (__node_type* __p = _M_find_node(__bkt, __k, __code)) {
     _M_deallocate_node(__node);
     return std::make_pair(iterator(__p), false);
  }
  return std::make_pair(_M_insert_unique_node(__bkt, __code, __node), true);
}
Run Code Online (Sandbox Code Playgroud)

因此,尽管我将使用第二种方法(即使它需要移动赋值和移动构造函数和额外的字段),但我想知道为什么 emplace 创建一个后来忽略的对象有很好的理由吗?也就是说,它是否应该首先检查它是否需要创建对象并在它已经存在时提前退出?

(请注意,对于我的特殊情况,默认初始化项不被认为是有效的,所以问题实际上只是关于 emplace)

作为记录,我在 23.2.4 表 102 下找到了一些内容:

E?ects: Inserts a value_type object t constructed with std::forward<Args>(args)...
if and only if there is no element in the container with key equivalent to the
key of t.
Run Code Online (Sandbox Code Playgroud)

我认为这将允许不创建对象。

nos*_*sid 4

在我看来,标准中引用的部分具有误导性,因为它表明,只有在容器中没有匹配元素的情况下才会构造对象。我猜他们是想表达:

效果:用构造一个value_type对象。当且仅当容器中不存在键等于 的键的元素时,插入构造的对象。tstd::forward<Args>(args)...tt

原因是:该函数的实现emplace必须构造t以找出是否存在具有等效键的元素,因为该实现必须调用哈希函数和equals谓词。但是,一般来说,它们只能使用 类型的对象来调用value_type,而不能使用用于构造这些对象的元组来调用。

理论上,可以指定一个emplacet函数,如果已经存在具有等效键的元素,则不会构造该函数。有趣的是,C++14 将为std::map::find. 请参阅以下文档:

只要比较函数满足一些附加要求,就有两个重载可以与任意类型一起使用。有趣的是,没有这样的重载std::unordered_map

  • 我认为 `std::piecewise_construct` 正是为了告诉键哪些参数。无论如何,我对此感到失望。似乎 unordered_map 不知道它实际上是一个键到值的映射 - 所以它无法仅操作键? (2认同)