【问题标题】:Past-the-end iterator invalidation in C++11C++11 中的尾端迭代器失效
【发布时间】:2012-07-06 05:15:23
【问题描述】:

C++ 上最受欢迎的帖子Iterator invalidation rules 声称不清楚过去的迭代器(即由end()cend()rend()crend() 返回的迭代器)是否无效根据与普通迭代器相同的规则,它们指向容器中的元素。这些声明适用于 2003 年和 2011 年 C++,请参阅讨论 End iterator invalidation rules 的帖子,其中公认的答案表明 2003 年标准在此问题上含糊不清。这个结论是基于 23.1/10 中的评论(在swap() 的上下文中),这似乎暗示当规范没有明确提到过去迭代器的失效时,它们可能会失效。

对该帖子问题的评论(由 mike-seymour 撰写)表明 C++11 在这个问题上是明确的,在 deques 的情况下。我的问题是关于所有容器的:

  • 在 C++11 中,是否有任何容器操作可能使过去的迭代器无效,并且这种行为在语言规范中是不明确的?

换个说法,

  • 在执行容器操作后,我是否可以相信过去迭代器的有效性,但该操作并未说明它可能会使过去的迭代器失效?

【问题讨论】:

  • 在执行不会使任何迭代器失效的操作后,您可以信任过去迭代器的有效性。对于所有其他情况,我不知道,我真的不在乎:重新获取迭代器(这是一个恒定时间操作)并且无需担心该特定迭代器是否无效......
  • 肯定有一些操作可能使结束迭代器无效。 vector::erase,例如,“在擦除点或之后使迭代器和引用无效。”结束迭代器必须在“擦除点之后”。
  • @dribeas:感谢您的解决方法;我确信它在几乎所有情况下都是最佳的,尤其是考虑到编译器内联等。有轶事证据 (stackoverflow.com/q/3084109/985943) 表明您可能会从缓存过去的指针中获得性能优势;确实这激发了我的问题。
  • @JamesMcNellis:同意。这里的标准是明确的,因为结束迭代器是在擦除点之后[定义在某处]。 vector::reserve(), 23.3.6.3/5(我有 rev. n3337)怎么样,“重新分配使所有引用序列中元素的...迭代器无效”,这个列表不包括过去的迭代器,根据定义,它不指向序列中的元素。然而,SGI 实现使 all 迭代器失效,大概也是过去的迭代器 [sgi.com/tech/stl/Vector.html#5].更多:stackoverflow.com/a/1624961/985943
  • 这肯定是 C++11 规范中的一个缺陷。

标签: c++ iterator


【解决方案1】:

我的问题是关于所有容器的:

  • 在 C++11 中,是否有任何容器操作可能使过去的迭代器无效,并且这种行为在 语言规范?

我不确定您所说的“这种行为在语言规范中不明确的地方”是什么意思,但肯定有一些操作会使结束后的操作符无效(例如插入到 std::vectorstd::string 中)。

换个说法,

  • 在执行容器操作后,我是否可以信任过去迭代器的有效性,但并未说它可能会失效 过去的迭代器?

您可以像任何其他迭代器一样信任过去的迭代器:任何不会(可能)使迭代器无效的操作都不会使它们无效。除了标准可能存在错误外,所有操作都没有说它们(可能)使操作员无效。

【讨论】:

  • 评论问题中reserve 的引用如何:"Reallocation invalidates all the...iterators referring to the elements in the sequence,"?你会说这绝对是标准的缺陷吗?
  • @Mark:可能是这样。常识表明,对于许多容器,使序列中的迭代器无效也必然会使结束迭代器无效。 (但是,我离语言律师的距离和高级 C++ 标签用户一样远,所以请谨慎对待。)
【解决方案2】:

如果标准说该操作不会使迭代器无效,您应该能够信任它。其他任何东西都应该被视为标准库实现中的错误。

【讨论】:

  • 我个人同意,对我的(第二个)问题的否定回答应该意味着标准中的错误。我同意你的回答,但我真的希望你能证明你所说的相反是错误的。也就是说,“您应该能够相信,如果标准没有说明,容器操作不会使任何迭代器(包括过去的迭代器)失效。”
【解决方案3】:

至少在 GCC 结束迭代器对 std::map 无效:

#include <set>
#include <stdlib.h>
#include <assert.h>
int main() {
  std::set<int> a;
  a.insert(1);
  std::set<int>::reverse_iterator rit(a.rbegin());
  ++rit;
  assert(rit==a.rend());
  a.erase(a.begin());
  assert(a.rend()==rit); // FAIL
}

【讨论】:

猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-12
  • 2017-10-05
  • 2023-01-13
相关资源
最近更新 更多