为啥允许 std::unordered_map::rehash() 使迭代器无效?

Posted

技术标签:

【中文标题】为啥允许 std::unordered_map::rehash() 使迭代器无效?【英文标题】:Why std::unordered_map::rehash() is allowed to invalidate iterators?为什么允许 std::unordered_map::rehash() 使迭代器无效? 【发布时间】:2020-10-03 20:24:03 【问题描述】:

Visual Studio (2015) 实现不会使任何迭代器失效(rehash() 重新排序内部 std::list,保持所有迭代器有效)。

是否有其他实现使迭代器无效(并获得更好的性能,即使具有相同的复杂性)?

std::unordered_map::rehash() 上使迭代器无效有时会限制开发人员(例如,我正在尝试仅使用 std::unordered_map 实现 LRU 缓存:节点的值具有容器的迭代器)。但是使迭代器无效的可能性是否允许更好地实现std::unordered_map::rehash()

【问题讨论】:

几乎所有标准容器类在变异操作中使迭代器无效,std::list 是个例外。 请注意,重新散列只会使迭代器无效,而不是指针或对元素的引用。由于重新散列改变了内部链表的顺序,这是有道理的。如果元素的顺序在此操作中间发生变化,您将如何定义迭代元素的操作? @Ilyan 我现在明白你的意思了。恐怕用std::unordered_map 实现LRU 缓存效率不高。我现在检查了有关此问题的多个问题,但所有建议的解决方案都在从缓存中逐出元素时触发了查找。这可能是您想要避免的。你需要的是一个指向unrodered_map node 的指针,它在重新散列后不会失效。但未提供此功能。 顺便说一句,您可以通过保留桶数来避免重新散列吗?设置为所需的 LRU 缓存大小加上设置最大负载因子? 如果 Microsoft STL 保证在重新散列后迭代器不会失效,我想这就是实现问题。不知道内部细节,但请注意,通常每个解决方案都有利有弊,因此 MS 实现可能会由于此设计决策而存在一些缺点。 【参考方案1】:

感谢@DanielLangr 的上述讨论,

我认为,此要求附带std::unordered_map::[const_]iterator 定义为LegacyForwardIterator(而不是LegacyBidirectionalIterator)。如果不使 rehash() 中的迭代器失效(可能,end() 除外),使用内部 std::forward_list 或类似的(而不是 VS 的 std::list)具有更好内存占用的实现是不可能的。

这种更好的内存占用实现可能会在这里和那里添加一些[简单的]汇编指令(也可能会在其他地方保存一些),或者可能有更大的sizeof(std::unordered_map::[const_]iterator),但定义std::unordered_map要求绝对是正确的原样,并将所有这些考虑因素留给实施者。

【讨论】:

以上是关于为啥允许 std::unordered_map::rehash() 使迭代器无效?的主要内容,如果未能解决你的问题,请参考以下文章

为啥我不能增加 std::unordered_map 迭代器?

为啥`std::unordered_map::erase(key_type const&)`返回删除元素的数量?

gcc std::unordered_map 实现速度慢吗?如果是这样 - 为啥?

C++ unordered_map emplace() 函数抛出段错误,我不知道为啥

C++ unordered_map 的基本使用

std::unordered_map::clear() 做啥?