reverse_iterators和base的令人费解的post和pre-increment行为

时间:2012-09-25 16:52:35

标签: c++ iterator

考虑玩具程序(post.cpp):

#include <iostream>
#include <vector>

using namespace std;
int main() {
        vector<int > a;
        int i;
        for(i=0;i<10;i++)
                a.push_back(i);
        auto it=a.rbegin();
        while(it!=a.rend()) {
                if ((*it % 2)==0) {
                                cout << "about to erase "<<*it<<endl;
                                a.erase((it++).base());
                }
                else {
                        ++it;
                }
        }
        for(auto it2=a.begin(); it2 != a.end(); it2++) {
                cout << *it2 << endl;
        }
        return 0;
}

我要做的是测试均匀度,然后删除当前数字,因为(it++)应返回当前迭代器,然后推进迭代器。这就是我得到的结果:

$ ./post 
about to erase 8
about to erase 6
about to erase 4
about to erase 2
about to erase 0
0
2
4
6
8

但是,如果我将行a.erase((it++).base());更改为a.erase((++it).base());,我会得到正确的行为。这是为什么?

有用的说明:我使用的是base(),因为在erase()中无法使用reverse_iterators。有一个应用程序,我想在向量上反向删除东西。

2 个答案:

答案 0 :(得分:2)

反向迭代器的base()偏移1.所以rbegin().base() == end()rend().base() == begin()

这只不过是这个反向循环的概括:

for (size_t i = 0; i != N; ++i)
{
    mutate(array[N - i - 1]);
}   //                 ^^^

循环以相反的顺序遍历数组,并注意我们在“迭代器”上需要- 1


更新:现在让我们来研究一下反向迭代器是什么:它只是一个双向迭代器的包装器。假设反向迭代器被称为it;然后它有一个普通的迭代器成员it.base()。例如,v.rbegin().base() == v.end()。当您说++it时,它只会调用--it.base()(概念上)。真正的魔力是取消引用操作:这必须在底层迭代器之前给我们一个元素:

*it == *(it.base() - 1)

这是完全相同的算法,它告诉我们来自数组的后面 i th 元素偏移一个:{ {1}}。这也向我们展示了为什么我们需要一个双向迭代器来形成反向迭代器。

现在很清楚我们如何通过反向迭代器从一个不会使迭代器失效的容器中删除它,例如任何基于节点的容器:

array[N - i - 1]

请记住,这要求擦除不会使除erasee之外的任何迭代器无效,就像在任何基于节点的容器中一样。从矢量中删除这样的事情会更加困难。对于向量,擦除会使所有迭代器无效,超过erasee(正向),因此我们必须使用if (meets_condition(*it)) // this examines *(it.base() - 1)! { auto b = it.base(); container.erase(it.base() - 1); it = std::reverse_iterator(b); } 函数的返回值:

erase

在图片中(我们删除元素“5”):

if (meets_condition(*ut))   // again, examine *(it.base() - 1)
{
    it = std::reverse_iterator(container.erase(it.base() - 1));
}

答案 1 :(得分:1)

不是您的问题的答案,而是您问题的解决方案:使用比您目前正在做的更有效的 erase-remove 习语:

bool even( int value ) { return !(value%2); }
std::vector<int> v = { 1,2,3,4,5,6,7,8,9,10 }; // Assuming C++11 or build it otherwise
v.erase( std::remove_if( v.begin(), v.end(), even ),
         v.end() );

效率的差异在于remove_if只会将容器中剩余的值复制到最终位置,而您的算法可能会多次复制某些元素。特别是9将被复制4次,7次复制3次等等。