【问题标题】:How do you iterate backwards through an STL list?你如何通过 STL 列表向后迭代?
【发布时间】:2010-09-16 09:06:52
【问题描述】:

我正在编写一些 Windows 和 Mac 之间的跨平台代码。

如果 list::end() “返回一个迭代器,该迭代器指向列表中最后一个元素之后的位置”并且可以在向前遍历列表时检查,那么向后遍历的最佳方法是什么?

此代码适用于 Mac,但不适用于 Windows(不能递减超过第一个元素):

list<DVFGfxObj*>::iterator iter = m_Objs.end();
for (iter--; iter!=m_Objs.end(); iter--)// By accident discovered that the iterator is circular ?
{
}

这适用于 Windows:

list<DVFGfxObj*>::iterator iter = m_Objs.end();
    do{
        iter--;
    } while (*iter != *m_Objs.begin());

是否有另一种可以在 for 循环中实现的向后遍历方式?

【问题讨论】:

  • 您的第一个示例(循环迭代器,与 end() 比较)会起作用,这只是实现的意外。

标签: c++ list stl iterator traversal


【解决方案1】:

使用reverse_iterator 而不是iterator。 使用rbegin() & rend() 而不是begin() & end()

如果您喜欢使用BOOST_FOREACH 宏,另一种可能是使用Boost 1.36.0 中引入的BOOST_REVERSE_FOREACH 宏。

【讨论】:

  • iterator 和 reverse_iterator 的文档几乎相同。迭代器是双向的,那么有什么区别?
  • 不同之处在于您仍然使用“++Iter”来递增迭代器,而不是使用“--Iter”。还是我错了?
  • 不,你是对的,向后增加有点奇怪,但也是有道理的。尽管考虑到迭代器是双向的,但 reverse_iterator 似乎没有必要。 reverse_iterator 的文档说它作用于反向列表;当然它不会首先在内部反转列表。
  • @AlanKey:如果你知道你正在处理一个列表,你可能只想减少普通迭代器。当您编写通用代码时,反向迭代器就会发挥作用——它不需要做任何特别的事情来向后遍历一个集合——它只需要被赋予反向迭代器
  • 并非所有“前向”迭代器都是“双向”的。这取决于集合类。
【解决方案2】:

反向迭代列表的最佳/最简单方法是(如前所述)使用反向迭代器 rbegin/rend。

但是,我确实想提一下,反向迭代器的实现是将“当前”迭代器位置逐一存储(至少在标准库的 GNU 实现中)。

这样做是为了简化实现,以便反向范围与正向范围 [begin, end) 和 [rbegin, rend) 具有相同的语义

这意味着取消引用迭代器涉及创建一个新的临时对象,然后将其递减,每次

  reference
  operator*() const
  {
_Iterator __tmp = current;
return *--__tmp;
  }

因此,解引用 reverse_iterator 比普通迭代器慢。

但是,您可以改为使用常规双向迭代器自己模拟反向迭代,避免这种开销:

for ( iterator current = end() ; current != begin() ; /* Do nothing */ )
{
    --current; // Unfortunately, you now need this here
    /* Do work */
    cout << *current << endl;
}

测试表明,对于循环主体中使用的每个取消引用,此解决方案的速度提高了约 5 倍。

注意:上面的代码没有进行测试,因为 std::cout 会成为瓶颈。

另请注意:“挂钟时间”差异约为 5 秒,std::list 大小为 1000 万个元素。因此,实际上,除非您的数据量很大,否则请坚持使用 rbegin() rend()!

【讨论】:

  • 再看这个,可能你只想用 current = --end(); 初始化并将增量步骤留在 for 循环内。这也可以防止我上面的版本没有的空数组。由于我还没有测试,所以我将暂时保留原帖。
  • 我不认为这会起作用,除非你也改变你的循环条件。否则你会遗漏第一项(如果current == begin(),循环不会执行)
  • for 循环的第一行会递减,所以是的,我想。只需编写一个快速测试即可尝试!无论如何,我不再认为这是使用这个习语的最佳方式,而且我最近运行的一些测试不再显示在完全优化下反向迭代器的显着速度改进。但是,仍然值得注意的是幕后发生的事情并进行了相应的测试!
  • 我打算评论您的评论,而不是答案,但所以删除了@-part。您答案中的代码工作得很好,但是,我同意它可能不是最好的;)编辑:快速test
【解决方案3】:

您可能需要反向迭代器。凭记忆:

list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin();
for( ; iter != m_Objs.rend(); ++iter)
{
}

【讨论】:

  • 谢谢,听起来不错。但是当迭代器被认为是双向的时,创建一个特殊的 reverse_iterator 似乎也是一种浪费
  • 应该是 "...>::reverse_iterator iter = ..."
  • @AlanKley 我认为您在问题中提出的 for 循环很好。我认为它有效的原因是因为 .end() 成员函数返回一个标记值,该值被分配为最后一个元素的下一个指针的值以及第一个元素上的 prev 指针。
  • 糟糕,重新阅读您的问题...在 Windows 上不起作用。我也在 Mac 上测试了代码。
【解决方案4】:

正如 Ferruccio 已经提到的,使用 reverse_iterator:

for (std::list<int>::reverse_iterator i = s.rbegin(); i != s.rend(); ++i)

【讨论】:

    【解决方案5】:

    这应该可行:

    list<DVFGfxObj*>::reverse_iterator iter = m_Objs.rbegin();
    for (; iter!= m_Objs.rend(); iter++)
    {
    }
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2015-09-25
      • 1970-01-01
      • 2011-07-19
      • 1970-01-01
      • 2013-09-23
      • 1970-01-01
      • 2018-03-19
      • 2017-06-02
      相关资源
      最近更新 更多