向后迭代

时间:2010-03-30 21:33:22

标签: c++ loops iterator

假设我有一个vector<int> myvec,我想反过来遍历所有元素。我可以想到几种方法:

for (vector<int>::iterator it = myvec.end() - 1; it >= myvec.begin(); --it)
{
    // do stuff here
}

for (vector<int>::reverse_iterator rit = myvec.rbegin(); rit != myvec.rend(); ++rit)
{
    // do stuff here
}

for (int i = myvec.size() - 1; i >= 0; --i)
{
    // do stuff here
}

所以我的问题是我什么时候应该使用它们?有区别吗?我知道第一个是危险的,因为如果我传入一个空的向量,那么myvec.end() - 1是未定义的,但是这还有其他危险或效率低下吗?

6 个答案:

答案 0 :(得分:11)

reverse_iterator版本显示意图并适用于所有容器,无论其内容如何。

第一个有你描述的缺陷。它还使用>=,这对非随机访问迭代器不起作用。

第三个问题是iint。它将无法保持size()可能返回的数量。使其无符号工作(vector<int>::size_type),但我们遇到与解决方案一样的问题。 (0U - 1 - &gt; Funky terminating checks - &gt; :|

答案 1 :(得分:7)

一般都不是以上。相反,你通常应该高枕无忧几秒钟,找出你想要应用的算法,而忘记自己编写一个循环。您可能会使用reverse_iterator,但根据您要完成的操作并非总是如此(例如,请参阅std::copy_backwards)。

答案 2 :(得分:3)

就个人而言,我会选择第二个。

正如您所指出的那样,第一个要求您将循环包装在if (!myvec.empty())中以避免未定义的行为。

对于最后一个,您应该使用vector<int>::size_typesize_t,在这种情况下>= 0是错误的,您需要执行!= (size_t)-1或类似操作

reverse_iterator版本更清晰。

答案 3 :(得分:2)

对于第一个版本,你也不可避免地会在循环结束时递减begin()迭代器(未定义的行为)。

reverse_iterator是为此做的。

如果您使用更有争议的形式,第三种方法可能会更好一些:

for (size_t i = vec.size(); i --> 0; )

如果人们停止抗拒,这可能是一个成语。它使用合适的计数器类型(无符号),并包含助记符,便于记忆和识别。

答案 4 :(得分:1)

始终使用第二个。第一个你排除了自己,第三个不适用于列表等。

答案 5 :(得分:1)

有第四种选择(不一定是一个好的选择,但它存在)。您可以以模仿反向迭代器实现方式的方式使用双向/随机访问迭代器,以避免空迭代器上myvec.end()-1的问题:

for (vector<int>::iterator it = myvec.end(); it != myvec.begin(); --it)
{
    // convert the loop controlling iterator to something that points
    //  to the item we're really referring to

    vector<int>::iterator true_it = it;
    --true_it;


    // do stuff here
    //  but always dereference `true_it` instead of `it`
    //  this is essentially similar to the way a reverse_iterator 
    //  generally works

    int& x = *true_it;
}

甚至:

for (vector<int>::iterator it = myvec.end(); it != myvec.begin();)
{
    // decrement `it` before the loop executes rather than after
    //  it's a bit non-idiomatic, but works
    --it;

    int& x = *it;

    // do stuff...
}

就像我说的那样,这不一定是一个好的选择(我认为Jerry Coffin's answer是你应该首先考虑的方法),但我认为它很有意义,因为它显示了反向迭代器如何在幕后工作 - 以及它避免了将reverse_iterator转换为迭代器的时间,因为你可能希望将迭代器用于不接受reverse_iterator的事情(将reverse_iterator转换为iterator s似乎让我头疼,所以我经常会避免reverse_iterators以避免头痛。例如,如果要为该位置调用insert(),则反向迭代器引用:

// if `it` is a reverse iterator, a call to insert might have to look something like:

myvec.insert( --(it.base()), 42 );  // assume that you know the current vector capacity
                                    //  will avoid a reallocation, so the loop's 
                                    //  iterators won't be invalidated

// if `it` is a normal iterator...

myvec.insert( it, 42 );