首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >std::unordered_map::emplace对象创建

std::unordered_map::emplace对象创建
EN

Stack Overflow用户
提问于 2014-05-16 21:19:16
回答 2查看 3.4K关注 0票数 6

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

代码语言:javascript
运行
复制
std::unordered_map<Key, Value> map;
map.emplace(
  std::piecewise_construct,
  std::forward_as_tuple(a),
  std::forward_as_tuple(b, c, d));

vs

代码语言:javascript
运行
复制
std::unordered_map<Key, DifferentValue> map;
auto& value = map[a];
if (value.isDefaultInitialized())
  value = DifferentValue(b, c, d);

我做了一些实验,看看谁的表现更好,发现当插入独特的元素时,行为(在效率方面)基本上是等同的。

但是,在插入重复项的情况下,并考虑到值或DifferentValue的构造并不简单,我惊讶地发现,不管它是否会插入对象,嵌入对象都会构造它。

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

对于emplace来说,守则似乎是:

代码语言:javascript
运行
复制
... _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);
}

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

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

为了记录在案,我在23.2.4表102下发现了一些东西:

代码语言:javascript
运行
复制
Effects: 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.

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

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2014-05-17 17:57:48

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

效果:使用value_type t构造std::forward<Args>(args)...对象。插入构造的对象t当且仅当容器中没有与t键等价的密钥的此类元素。

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

理论上,可以指定一个emplace函数,如果已经有一个具有等效键的元素,就不会构造t。有趣的是,将在C++14 for std::map::find中添加类似的内容。见下列文件:

有两个重载,可以用于任意类型,只要比较函数满足一些额外的要求。有趣的是,std::unordered_map没有这样的过载。

票数 4
EN

Stack Overflow用户

发布于 2019-01-19 00:53:56

是的,std::unordered_map::emplace()所做的第一件事是在搜索之前,在内存中创建要放置的键-值对,如果表中已经存在带有刚刚构造的键的元素。如果找到这样的元素,emplace()将继续,立即再次销毁新创建的元素。这通常不是人们首先使用emplace()的原因,因为它是为了避免不必要的对象创建!

std::(unordered_)map::emplace()的破坏设计背后的原因可能是,如果找不到键,那么首先创建键,然后检查密钥是否存在的实现,需要能够移动或复制该键在键值对中的最终目的地。由于emplace()是专门为不可复制的不可移动对象添加到STL容器中的,因此依赖于移动/可复制键的emplace实现将是不完整的。

然而,99%的合理键要么是可复制的,要么是可移动的,或者两者兼而有之,因此它们应该与值分开处理,其结构可能要复杂得多。对于C++17 ( C++1z ),该语言的神明意味着它对我们很好,并添加了try_emplace()方法:它的参数是对已经构造的键的引用,以及仅在位置构造相应值所需的参数。try_emplace()首先搜索密钥。只有当键是新的时,一个新的键-值对是通过复制或移动键并构造相应的值来构造的。万岁!

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/23704706

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档