如何向后遍历 STL 列表?

How do you iterate backwards through an STL list?

提问人:AlanKley 提问时间:10/10/2008 最后编辑:Abderrahmene Rayene MihoubAlanKley 更新时间:11/17/2023 访问量:60182

问:

我正在 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 循环中实现的向后遍历方法?

C++ List STL 迭代器 遍历

评论

2赞 Justsalt 10/10/2008
您的第一个示例(循环迭代器,与 end() 进行比较)会起作用只是实现的意外。

答:

66赞 Ferruccio 10/10/2008 #1

使用 代替 。 使用 & 而不是 & 。reverse_iteratoriteratorrbegin()rend()begin()end()

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

评论

0赞 AlanKley 10/10/2008
迭代器和reverse_iterator的文档几乎相同。迭代器是双向的,那么差异是什么?
2赞 steffenj 10/10/2008
不同之处在于,您仍然执行“++Iter”来递增迭代器,而不是“--Iter”。还是我错了?
0赞 AlanKley 10/10/2008
不,你是对的,递增向后退有点奇怪,但也是有道理的。尽管reverse_iterator似乎是不必要的,因为迭代器是双向的。reverse_iterator 的文档说它作用于一个反向列表;当然,它不会首先在内部反转列表。
0赞 Michael Burr 10/10/2008
@AlanKey:如果你知道你正在处理一个列表,你可能只想递减正常的迭代器。当你编写泛型代码时,反向迭代器就会发挥作用——它不需要做任何特别的事情来反向遍历一个集合——它只需要被赋予反向迭代器
2赞 Jesse Chisholm 10/17/2013
并非所有“前向”迭代器都是“双向”的。这取决于集合类。
14赞 Anthony Cramp 10/10/2008 #2

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

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

评论

2赞 AlanKley 10/10/2008
谢谢,听起来不错。但是,当迭代器被认为是双向的时,创建一个特殊的reverse_iterator似乎也是一种浪费
0赞 steffenj 10/10/2008
它应该是“......>::reverse_iterator iter = ...”
0赞 Anthony Cramp 10/10/2008
@AlanKley我认为您在问题中放入的 for 循环很好。我认为它起作用的原因是因为 .end() 成员函数返回一个哨兵值,该值被指定为最后一个元素的下一个指针的值以及第一个元素上的上一个指针的值。
0赞 Anthony Cramp 10/10/2008
哎呀,重读你的问题......在 Windows 上不起作用。我也在 Mac 上测试了代码。
0赞 Some Guy 8/27/2022
在 C++ 11 及更高版本中,如果您在 for 循环中使用 “”,而不是在 for 循环之外使用其完整类型签名声明迭代器,则此代码读起来会更清晰。它还会将迭代器的范围限制在循环中,这通常是人们想要做的。for (auto iter=m_Objs.rbegin(); iter != m_Objs.rend(); ++iter)
7赞 ejgottl 10/10/2008 #3

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

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

评论

0赞 Some Guy 8/27/2022
在 C++ 11 及更高版本中,如果您在 for 循环中使用 “” 而不是使用其完整类型签名声明迭代器,则此代码读起来会更清晰。for (auto iter=m_Objs.rbegin(); iter != m_Objs.rend(); ++iter)
5赞 steffenj 10/10/2008 #4

这应该有效:

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

评论

1赞 Some Guy 8/27/2022
在 C++ 11 及更高版本中,如果您在 for 循环中使用 “”,而不是在 for 循环之外使用其完整类型签名声明迭代器,则此代码读起来会更清晰。它还会将迭代器的范围限制在循环中,这通常是人们想要做的。for (auto iter=m_Objs.rbegin(); iter != m_Objs.rend(); ++iter)
18赞 mmocny 10/22/2008 #5

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

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

这样做是为了简化实现,以便反向范围与范围 forward [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()!

评论

1赞 mmocny 10/23/2013
再看一遍,可能你想用 current = --end() 进行初始化;并将增量步骤保留在 for 循环中。这也可以防止空数组,而我上面的版本没有。由于我还没有测试,我现在将保留原始发布。
0赞 Gerrit-K 7/21/2014
我不认为这行得通,除非你也改变你的循环条件。否则,您将丢失第一项(如果出现以下情况,则导致循环无法执行current == begin())
0赞 mmocny 7/21/2014
for 循环的第一行会递减,所以我认为是的,它会递减。只需编写一个快速测试即可尝试一下!无论如何,我不再认为这是使用这个成语的最佳方式,而且我最近运行的一些测试不再显示在完全优化下反向迭代器的速度显着提高。但是,仍然值得注意引擎盖下发生的事情并进行相应的测试!
1赞 Gerrit-K 7/22/2014
我打算评论您的评论,而不是答案,但 SO 删除了 @-部分。您回答中的代码运行良好,但是,我同意它可能不是最好的;)编辑:快速测试