【问题标题】:STL iterator revalidation for end (past-the-end) iterator?结束(过去)迭代器的 STL 迭代器重新验证?
【发布时间】:2019-04-19 13:45:01
【问题描述】:

查看有关过去迭代器失效的相关问题: this, this.

这更多的是一个设计问题,即是否存在(在 STL 或其他地方)过去的迭代器“重新验证”这样的概念

我的意思和用例:假设一个算法需要“尾随”一个容器(例如队列)。它遍历容器直到到达end(),然后暂停;与此无关,程序的另一部分将更多项目排入队列。算法怎么可能(编辑)有效地告诉“有更多的项目已入队”,同时持有以前的结束迭代器(称之为tailIt)? (这意味着它能够检查tailIt == container.end() 是否仍然 如果这是错误的,则得出结论tailIt 现在有效并指向插入的第一个元素)。

请不要将这个问题视为“不,没有” - 我正在寻找关于如何以惯用方式设计一些逻辑的判断,并且有很多选择(实际上有问题的迭代器是到我可以提供此属性的手工构建的数据结构 - end() revalidation - 但我想判断这是否是个好主意)。 p>


编辑:明确我们有迭代器tailIt container 的引用。我正在尝试做的一个简单的解决方法是,还记得count := 您处理了多少项目,然后检查仍然是container.size() == count,如果不是,请寻求container[count] 并从那里继续处理。这带来了许多缺点(额外的状态,假设容器不会从前面弹出(!),随机访问以实现高效搜索)。

【问题讨论】:

  • 简单的例子。假设您有一个std::vector<T> 和一个迭代器T* p,它们是指向末尾的原始指针(= end())。然后你在没有重新分配的情况下扩展向量。如果你只有p的值,你能确定向量是否被扩展了吗?
  • 谢谢 - 你不会,但我需要澄清一些事情。你也有容器c,所以你可以问p == c.end() 还在吗?对于向量情况,只有没有重新分配,p 才会指向第一个新插入的元素。编辑问题。

标签: c++ algorithm stl iterator invalidation


【解决方案1】:

一般不会。以下是您的想法的一些问题:

  • 一些过去的迭代器根本不“指向”数据块;事实上,除了向量迭代器之外,any 迭代器都是如此。因此,总的来说,现存的终结迭代器永远不会成为数据的有效迭代器;
  • Iterators often become invalidated when the container changes — 虽然这并不总是正确的,但它也排除了依赖于从突变之前取消引用某些迭代器的通用解决方案;
  • 迭代器有效性是不可观察的——在取消引用迭代器之前,您已经需要知道它是否有效。这是来自其他地方的信息,通常是你的大脑……我的意思是开发人员必须阅读代码并根据其结构和流程做出决定。

将所有这些放在一起,很明显,最终迭代器根本不能以这种方式使用,因为当前设计了迭代器接口。迭代器引用范围内的数据,而不是容器;因此,有理由认为它们不包含有关容器的信息,并且如果容器导致范围发生更改,则迭代器不知道任何实体可以要求找出这一点。

是否可以创建所描述的逻辑?当然!但是使用不同的迭代器接口(以及来自容器的支持)。您可以将容器包装在您自己的类类型中来执行此操作。但是,我建议不要制作看起来像标准迭代器但行为不同的东西。这会很混乱。

相反,封装容器并提供您自己的包装函数,该函数可以直接执行您认为需要的任何入队后操作。您无需查看结束迭代器的状态即可实现您的目标。

【讨论】:

    【解决方案2】:

    在 std::queue 的情况下,没有(呵呵)。不是因为队列的迭代器在推送某些内容后失效,而是因为队列根本没有任何迭代器。

    至于其他迭代器类型,它们中的大多数(或其中任何一个)不需要对容器持有者(包含有关基础数据的所有信息的管理对象)的引用。这是效率与灵活性的权衡。 (我很快检查了 gcc 的 std::vector::iterator 的实现)
    可以为迭代器类型编写一个实现,在其生命周期内保持对持有者的引用,这样迭代器就不必失效! (除非持有人是 std::move'd)

    现在发表我的专业意见,对于迭代器通常会在迭代期间失效的情况,我不介意看到 safe_iterator/flex_iterator。

    可能的用户界面:

    for (auto v : make_flex_iterator(my_vector)) {
        if (some_outside_condition()) {
            // Normally the vector would be invalidated at this point
            // (only if resized, but you should always assume a resize)
            my_vector.push_back("hello world!");
        }
    }
    

    从字面上重新验证迭代器可能太复杂而无法构建它的用例(我不知道从哪里开始),但是设计一个永远不会失效的迭代器非常简单,只需与 for (size_t i = 0; i < c.size(); i++); 循环一样多的开销.
    但话虽如此,我无法向您保证编译器将如何优化这些迭代器,例如展开循环。我确实认为它仍然会做得很好。

    【讨论】:

      猜你喜欢
      • 2011-08-22
      • 1970-01-01
      • 2013-02-21
      • 1970-01-01
      • 2015-10-28
      • 2011-07-04
      • 2012-08-16
      • 2013-04-16
      • 1970-01-01
      相关资源
      最近更新 更多