这是正确的行为吗?std::map 迭代器失效


#include <iostream>
#include <map>
int main(int argc, char** argv)
{
  std::map<int, int> map;
  map.emplace(1, 1);
  auto reverse_iter = map.rbegin();
  std::cout << reverse_iter->first << ", " << reverse_iter->second << std::endl;
  map.emplace(2, 2);
  std::cout << reverse_iter->first << ", " << reverse_iter->second << std::endl;
  return 0;
}

这将打印出来:

1, 1
2, 2

根据标准,这真的应该发生吗? 我没有触及reverse_iter但它所指向的价值正在发生变化。我认为 std::map 中的迭代器应该可以安全地防止插入。然而,它似乎决定reverse_iter不是继续指向我告诉它的值,而是"此时地图末尾发生的任何内容"。

更新:更多信息,以防万一:前向迭代器似乎不会发生这种情况(在我似乎可以找到的任何情况下(,我的 gcc 版本是 5.1.1-4。

根据C++标准(23.2.4 关联容器(

9 插入和放置构件不影响 容器的迭代器和引用,擦除成员应 仅使迭代器和对已拭除元素的引用失效。

另一方面(24.5.1 反向迭代器(

1 类模板 reverse_iterator 是迭代的迭代器适配器 从其底层迭代器定义的序列的末尾到 该序列的开头。

虽然在最后的引文中提到了类std::reverse_iterator但对于标准容器的反向迭代器也是如此。

根据表 97 — 可逆容器要求

rbegin()对应于reverse_iterator(end())

所以在你的例子中,反向迭代器仍然对应于end() .`

正如这里引用的 http://en.cppreference.com/w/cpp/container/map/rbegin

反向迭代器

将迭代器存储到下一个元素,而不是它实际引用的元素

副作用是,如果您在该迭代器(倾斜 end(((之前插入某些内容,当您取消引用该反向迭代器时,您将看到该新值。我不认为在这种情况下反向迭代器是无效的。

map.rbegin()返回一个等于 std::reverse_iterator(map.end()); 的迭代器

取消引用反向迭代器时会出现此问题。当您取消引用reverse_iterator时,您实际获得的值来自迭代器,然后再存储在reverse_iterator中。这可能看起来很奇怪,但有充分的理由,这是不可避免的。 为了安排范围的过去结束元素,就是这样:指向区域中过去结束元素的迭代器在反转时指向范围的最后一个元素(而不是超过它((这将是反转范围的第一个元素(。如果对区域中第一个元素的迭代器被反转,则反转的迭代器指向第一个元素之前的元素(这将是反转范围的过去元素(。

也就是说,在您的情况下,取消引用reverse_iter等效于执行以下操作:

*(--map.end());

因此,在第二次emplace之后,地图的最后一个元素发生了变化,取消引用(--map.end())(即您的reverse_iter(,您将获得地图中新的最后一个元素。

std::map<int, int> map2;
map2.emplace(2, 2);
auto fiter = map2.begin();
std::cout << fiter->first << ", " << fiter->second << std::endl;
map2.emplace(1, 1);
std::cout << fiter->first << ", " << fiter->second << std::endl;
fiter = map2.begin();
std::cout << fiter->first << ", " << fiter->second << std::endl;

指纹

2, 2
2, 2
1, 1

最新更新