【问题标题】:Will elements added into a std::unordered_set (or unordered_map) during iteration be visited during the iterations?迭代期间添加到 std::unordered_set(或 unordered_map)中的元素是否会在迭代期间被访问?
【发布时间】:2020-05-24 05:48:49
【问题描述】:

我的代码如下所示:

std::unordered_set<int> ht{1,2,3};
ht.reserve(10000);  // ht will not exceed this size

for(int i = 0; i < n; i++)
{ 
  auto j = i;
  for(auto it = ht.begin(); it != ht.end(); ++it)
  {
    // do some stuff
    int v = j++;
    ht.emplace(v);
  }
}

对于内部循环,我想从 ht 的开头循环到结尾,但我不希望循环遍历循环中任何新添加的元素。换句话说,上面是否等同于下面?

std::unordered_set<int> ht{1,2,3};
ht.reserve(10000);  // ht will not exceed this size

for(int i = 0; i < n; i++)
{
  auto temp = ht;
  auto j = i;
  for(auto it = ht.begin(); it != ht.end(); ++it)
  {
    // do some stuff
    auto v = j++;
    temp.emplace(j);
  }

  ht = temp;
}

根据我所做的几次运行,它似乎是等效的,但我不知道这是否是未定义的行为,或者它们是否确实是等效的。如果 unordered_set 更改为 vector,这将不起作用,但似乎前向迭代器工作。

如果ht.reserve(10000); // ht will not exceed this size 不存在或ht 实际上超出了保留容量,答案是否会改变,因此所有前向迭代器都将失效?

【问题讨论】:

  • std::unordered_set::reservestd::vector::reserve 做的事情不同。它不仅仅是分配空间。另外 -- 基于我做的几次运行, -- C++ 不能以这种方式工作,您可以通过几次运行来验证(或不验证)某些东西是安全的。它要么是未定义的行为,要么是已定义的行为,要么是实现定义的行为。
  • @J.Schultke 我想是这样,但我只是想确认我的理解是正确的。根据阅读 GManNickG 的回答,似乎即使我们保证不重新散列,我们仍然可以迭代新添加的元素?
  • @PaulMcKenzie 是的,我同意这两点。但是std::unordered_set::reserve(N) 会保证 unordered_set 至少可以存储N 数字而无需重新散列对吗?如果没有冲突,它保证它可以准确存储N 数字。

标签: c++ for-loop unordered-set


【解决方案1】:

不,这不安全:

在大多数情况下,容器中的所有迭代器在插入后仍然有效。唯一的例外是当容器的增长迫使重新散列时。在这种情况下,容器中的所有迭代器都会失效。

有时它会起作用,但我认为这对你来说还不够!

【讨论】:

  • 我认为OP的问题是关于在迭代器遍历期间是否会看到新添加的元素,而不是关于失效;他们已经使用了储备,因此不会发生重新哈希。
  • @templatetypedef 是的,没错。我基本上是在假设没有重新散列发生(由于保留声明并且不超过容量)的基础上提出问题的第一部分
  • 对不起,我错过了保留。
【解决方案2】:

没有。见cppreference on std::unordered_set

Cppreference.com 有迭代器失效部分,用于描述迭代器何时失效。在使用std::unordered_set的情况下,发生rehash时插入是不安全的。

仅当新的元素数量大于max_load_factor()*bucket_count()时才会发生重新散列

而且您无法确定插入元素是否会导致这种情况发生。

在您的示例中,您实际上并没有取消对迭代器的引用,那么为什么不循环遍历集合的大小呢?

size_t limit = ht.size();
for (size_t i = 0; i < limit; ++i) {
    ...
}

【讨论】:

  • 我认为OP的问题是关于在迭代器遍历期间是否会看到新添加的元素,而不是关于失效;他们使用了储备,这样就不会发生重新哈希。
  • 对,我知道如果发生重新散列,所有迭代器都会失效,但这就是我包含reserve 和相关评论的原因。
猜你喜欢
  • 1970-01-01
  • 2016-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-12-05
  • 2013-09-01
相关资源
最近更新 更多