【问题标题】:Confusion on iterators invalidation in deque对双端队列中的迭代器失效感到困惑
【发布时间】:2010-10-29 04:15:06
【问题描述】:

我对双端队列中的迭代器失效感到有点困惑。 (在this问题的上下文中)

以下是摘自 -- C++ 标准库:教程和参考, 尼古拉 M. Josuttis

任何元素的插入或删除 其他在开头或结尾 使所有指针、引用无效, 和引用元素的迭代器 双端队列的。

以下是SGI网站的摘录:

迭代器失效的语义 对于双端队列如下。插入 (包括push_frontpush_back) 使所有引用的迭代器无效 到双端队列。在 a 中间擦除 deque 使所有迭代器失效 参考双端队列。擦除在 双端队列的开始或结束(包括 pop_frontpop_back) 使 迭代器仅当它指向 删除元素。

恕我直言,双端队列是块的集合,第一个块在一个方向上增长,最后一个块在相反方向上增长。

  -   -  -  
  -   -  -
  |   -  -  ^
  |   -  -  |
  V   -  -  |
      -  -  -
      -  -  -

push_back, push_front 不应该对双端队列迭代器产生任何影响(我同意 Josuttis 的观点)。

正确的解释是什么?标准对此有何规定?

【问题讨论】:

    标签: c++ stl standards deque


    【解决方案1】:

    SGI 实现可能使用了可增长的数组,因此如果插入导致数组增长,则指向旧数组的迭代器无效。

    编辑:

    查看 The C++ Programming Language Third Edition 的第 17.2.3 节,我在 deque 的描述中没有看到任何指示哪些操作保留或使迭代器无效的内容。我可能看错了地方,或者行为可能未定义。

    【讨论】:

      【解决方案2】:

      来自standard working draft

      模板 无效插入(迭代器位置, InputIterator first , InputIterator last );

      1 效果:中间插入 双端队列的所有 迭代器和对元素的引用 双端队列的。两端插入 双端队列的所有 双端队列的迭代器,但没有 对参考文献有效性的影响 到双端队列的元素。”

      所以两者都是正确的。正如 Josuttis 所指出的,在前面或后面的插入不会使对 deque 元素的引用无效,只会使 deque 本身的迭代器

      编辑:more up-to-date draft 说的基本相同(第 23.2.2.3 节)

      【讨论】:

        【解决方案3】:

        恕我直言,双端队列是块的集合,第一个块在一个方向上增长,最后一个块在相反方向上增长。

        您的意见是您的特权,但这是错误的。 :)

        deque语义上就是这样一个容器,但就实现而言,它被设计为由一个或多个内存块实现。 C++'s iterator invalidation rules 来自实现,所以这就是原因。可以说这是一个小的抽象泄漏,但是,无论如何。

        SGI STL 文档不适合阅读,因为the SGI STL is not the C++ Standard Library。不幸的是,Josuttis 是那些称其为“STL”的人之一,这导致了您的困惑。


        以下是摘自 -- The C++ Standard Library: A Tutorial and Reference,作者 Nicolai M. Josuttis

        在开头或结尾插入或删除元素之外的任何元素都会使引用双端队列元素的所有指针、引用和迭代器无效。

        简而言之,Josuttis 的这段话误导,暗示插入或删除 在开头或结尾的元素并不不是 使指针、引用或迭代器无效……尽管值得注意的是,他从来没有站出来直接断言这一点。


        这是std::deque的真实、正确、官方的规则:

        C++03

        • 插入:所有迭代器和引用都无效,除非插入的成员位于双端队列的末尾(前或后)(在这种情况下,所有迭代器都无效,但对元素的引用不受影响) [23.2.1.3/1]

        • 擦除:所有迭代器和引用都无效,除非被擦除的成员位于双端队列的末尾(前面或后面)(在这种情况下,只有迭代器和对被擦除成员的引用无效) [23.2.1.3/4]

        • 调整大小:根据插入/擦除 [23.2.1.2/1]

        C++11

        • 插入:所有迭代器和引用都无效,除非插入的成员位于双端队列的末尾(前或后)(在这种情况下,所有迭代器都无效,但对元素的引用不受影响) [23.3.3.4/1]

        • 擦除:擦除最后一个元素只会使迭代器和对被擦除元素的引用和过去的迭代器无效;擦除第一个元素只会使迭代器和对被擦除元素的引用无效;擦除任何其他元素会使所有迭代器和引用(包括过去的迭代器)无效 [23.3.3.4/4]

        • 调整大小:根据插入/擦除 [23.3.3.4/1]


        进一步阅读

        我不确定您要寻找的可靠来源的进一步参考 - 相关标准段落已被引用和引用。

        【讨论】:

        • 啊,这正是我所希望的,谢谢。现有的答案真的很令人困惑(例如“双端队列本身的迭代器”真的让我失望),这清除了它。 :)
        • @Mehrdad:基本上是it.begin() vs *it.begin() ;)
        • 您的意思是 deque.begin()*deque.begin()? :P
        • 为什么? Object &obj = deque[5]; obj.foo(); deque.push_back(Object()); obj.bar(); 对我来说似乎完全合理。
        • @Mehrdad,与stable_vector 相比,deque 牺牲了迭代器的完整性以换取不添加更多的间接。 stable_vector 实现必须与数据一起维护对控件数组的反向引用,并且它的迭代器需要查看此反向引用以发现数组的位置,而不是直接将指针存储到数组中。跨度>
        猜你喜欢
        • 1970-01-01
        • 2010-12-12
        • 2016-04-08
        • 1970-01-01
        • 2022-11-25
        • 1970-01-01
        • 2015-12-24
        • 2012-10-14
        • 1970-01-01
        相关资源
        最近更新 更多