STL迭代器重新验证结束(过去)迭代器?

时间:2018-11-16 11:09:40

标签: c++ algorithm stl iterator invalidation

请参阅有关过去的迭代器无效的相关问题: thisthis

这更多是设计问题,即(在STL或其他地方)是否存在诸如最后的迭代器“重新验证” 这样的概念?

这是什么意思,用例:假设算法需要“尾巴”一个容器(例如队列)。它遍历容器,直到到达end(),然后暂停;与此无关,程序的另一部分将更多项目放入队列中。该算法如何在保持先前的过去的迭代器(称为tailIt)的同时(edit)有效地告诉“有更多的项目被排队”? (这意味着它能够检查tailIt == container.end() still ,如果为假,则得出结论tailIt现在有效并指向插入的第一个元素)。

请不要将问题视为“不,不存在”-我希望就如何以惯用的方式设计一些逻辑形成判断,并且有很多选择(实际上有问题的迭代器是到我可以为其提供此属性的手工构建数据结构- end()重新验证-但我想判断它是否是一个好主意)。


编辑:明确表示,我们拥有迭代器tailIt 和对container的引用。对于我要执行的操作,一个简单的解决方法是,还记得count:=处理了多少个项目,然后检查是否仍然container.size() == count,如果没有,请尝试container[count]并从那里继续处理。这样做有很多缺点(额外的状态,假设容器没有从前面弹出(!),为有效查找而随机访问)。

2 个答案:

答案 0 :(得分:5)

一般而言。这是您的想法中的一些问题:

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

将所有这些放在一起,很显然,最终迭代器根本无法以这种方式使用,因为当前正在设计迭代器接口。迭代器引用范围内的数据,而不是容器。因此,有理由要说的是,它们不保存有关容器的信息,并且如果容器导致范围更改,那么迭代器就不会知道它可以请求找出该对象的任何实体。

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

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

答案 1 :(得分:1)

在使用std :: queue的情况下,没有(嘿)。并不是因为队列中的迭代器一旦被推送就变得无效,而是因为队列中根本没有任何迭代器。

对于其他迭代器类型,大多数(或其中任何一种)不需要引用容器持有者(包含有关基础数据的所有信息的管理对象)。这是效率与灵活性之间的权衡。 (我很快检查了gcc的std :: vector :: iterator的实现)
可以为迭代器类型编写一个实现,该实现在其生命周期内保留对Holder的引用,这样,迭代器就不必无效! (除非持有人为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!");
    }
}

从本质上讲,重新验证迭代器对于使用案例而言可能太复杂了(我不知道从哪里开始),但是设计一个永不失效的迭代器非常琐碎,其开销只有{{1} }循环。
但是,我不能保证您使用这些迭代器优化编译器的优化程度,例如展开循环。我确实认为它仍然会做得很好。