如何从stl数据结构中删除reverse_iterator?

时间:2008-12-31 23:18:28

标签: c++ stl iterator

由于某种原因,以下代码失败。您不能简单地使用其base()方法擦除reverse_iterator。

#include <set>
#include <iostream>

int main()
{
    std::set<int> setOfInts;
    setOfInts.insert(1);
    setOfInts.insert(2);
    setOfInts.insert(3);

    std::set<int>::reverse_iterator rev_iter = setOfInts.rbegin();
    std::set<int>::reverse_iterator nextRevIter = setOfInts.rbegin();
    ++nextIter;

    while ( rev_iter != setOfInts.rend())
    {
        // Find 3 and try to erase
        if (*rev_iter == 3)
        {
            // SEGFAULT HERE
            setOfInts.erase( rev_iter.base());
        }
        rev_iter = nextRevIter;
        ++nextRevIter;
    }

}

如何正确地完成上述操作?如果reverse_iterator对应于您要擦除的内容,那么如何删除它?

注意,不幸的是,擦除不会使用reverse_iterator。它想要真实的东西。

4 个答案:

答案 0 :(得分:18)

显然,解决方案是base()返回的是1。对于reverse_iterator,以下标识成立:

&*(reverse_iterator(i)) == &*(i - 1) 

或者换句话说,reverse_iterator始终是一个通过它的基础的常规迭代器。不知道为什么。

在GCC

只需更改

        // SEGFAULT HERE
        setOfInts.erase( rev_iter.base());

        // WORKS!
        setOfInts.erase( --rev_iter.base());

我很清楚,为什么上面的身份才有意义。

在Visual Studio中

回到工作中并在visual studio中尝试这个,我看到上面的解决方案不太有效。擦除时“nextIter”变为无效。相反,你需要将擦除中的临时值保存起来以获得下一个迭代器,而不是保持像上面那样的nextIter。

  set<int>::iterator tempIter = setOfInts.erase(--rev_iter.base());
  rev_iter = setOfInts.erase(tempIter);

所以最终的解决方案是

int main()
{
    using namespace std;

    set<int> setOfInts;
    setOfInts.insert(1);
    setOfInts.insert(2);
    setOfInts.insert(3);

    set<int>::reverse_iterator rev_iter = setOfInts.rbegin();

    while ( rev_iter != setOfInts.rend())
    {
        // Find 3 and try to erase
        if (*rev_iter == 3)
        {
            cout << "Erasing : " << *rev_iter;
            set<int>::iterator tempIter = setOfInts.erase( --rev_iter.base());
            rev_iter = set<int>::reverse_iterator(tempIter);            
        }
        else
        {
            ++rev_iter;
        }
    }   

}

注意,关联容器不会从erase返回迭代器。所以这个解决方案不适用于map,multimap等。

答案 1 :(得分:3)

当您使用反向迭代器进行迭代并希望使用base()来修改其容器时,请始终记住,reverse_iterator始终基于原始顺序中的下一个迭代器。这有点不直观,但它实际上使代码更简单:

#include <set>
int main()
{
    std::set<int> setOfInts;
    setOfInts.insert(1);
    setOfInts.insert(2);
    setOfInts.insert(3);

    typedef std::set<int>::reverse_iterator RevIter;

    RevIter rev_iter = setOfInts.rbegin();
    while (rev_iter != setOfInts.rend())
    {
        // Find 3 and try to erase
        if (*rev_iter == 3)
            setOfInts.erase(--rev_iter.base());

        ++rev_iter;
    }
}

在这个例子中,不需要保留“下一个”迭代器,因为基本迭代器没有失效! (在处理普通迭代器时我们确实需要它。)

反向迭代器的行为在处理单个项目时会产生奇怪的一对一困难,但事实上它简化了范围:

riValue = find(riEnd.base(), riBegin.base(), value);

使用与

完全相同的对象(顺序相反)
iValue = find(riBegin, riEnd, value);

答案 2 :(得分:0)

来自map::erase

1,我们知道只需要iterator;

来自reverse_iterator::base

2,我们知道&*(reverse_iterator ( i ) ) == &*( i – 1 ).

因此,您可以擦除( - r_v.base())以擦除“r_v”(和“current-1”)指向的元素:

            r_v+1            r_v          r_v-1
           current-2      current-1      current

答案 3 :(得分:-3)

使用迭代器本身调用erase(无需使用base)。

#include <set>
#include <iostream>

int main()
{
    std::set<int> setOfInts;
    setOfInts.insert(1);
    setOfInts.insert(2);
    setOfInts.insert(3);

    std::set<int>::reverse_iterator rev_iter = setOfInts.rbegin();

    while (rev_iter != setOfInts.rend())
    {
        // Find 3 and try to erase
        if (*rev_iter == 3)
        {
            rev_iter = setOfInts.erase(rev_iter);
        }
        else
        {
            ++rev_iter;
        }
    }
}

此外,您不需要单独的“下一个”迭代器(请参阅上面的更改)。更好的方法是使用std::remove_if(或类似函数)。