我正在选择将事物放入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));
vs
std::unordered_map<Key, DifferentValue> map;
auto& value = map[a];
if (value.isDefaultInitialized())
value = DifferentValue(b, c, d);
我做了一些实验,看看谁的表现更好,发现当插入独特的元素时,行为(在效率方面)基本上是等同的。
但是,在插入重复项的情况下,并考虑到值或DifferentValue的构造并不简单,我惊讶地发现,不管它是否会插入对象,嵌入对象都会构造它。
因此,在这种情况下,第二种方法似乎获胜了,因为默认构造函数中只有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);
}
所以,尽管我将使用第二个方法(即使它需要移动赋值和移动构造函数以及额外的字段),但我想知道为什么emplace会创建一个后来忽略的对象,这有一个很好的理由吗?也就是说,它是否应该首先检查是否需要创建对象,如果它已经存在,是否应该提前退出?
(请注意,对于我的特殊情况,默认初始化项不被认为有效,因此问题实际上只是关于emplace)
为了记录在案,我在23.2.4表102下发现了一些东西:
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.
我认为这将允许不创建对象。
发布于 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
没有这样的过载。
发布于 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()首先搜索密钥。只有当键是新的时,一个新的键-值对是通过复制或移动键并构造相应的值来构造的。万岁!
https://stackoverflow.com/questions/23704706
复制相似问题