Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How do I erase a reverse_iterator from an stl data structure?

Tags:

c++

iterator

stl

For some reason the following code fails. You can't simply erase a reverse_iterator by using its base() method.

#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;
    }

}

How does one go about correctly doing the above? Given a reverse_iterator that corresponds to something you want to erase, how do you erase it?

Note, erase won't take reverse_iterators unfortunately. It wants the real thing.

like image 804
Doug T. Avatar asked Dec 31 '08 23:12

Doug T.


People also ask

How do I delete a STL list?

list::clear() is an inbuilt function in C++ STL which is declared in header file. list::clear(), clears the whole list. In other words the clear() removes all the elements present in the list container and leaves the container with size 0.

What erase function returns?

Return Value: This function returns an iterator pointing to the element in the list container which followed the last element erased from the list container. Below programs illustrates the list::erase() function. Program 1: Erasing a single element. CPP.

What is reverse iterator in C++?

C++ Iterators Reverse Iterators A reverse iterator is made from a bidirectional, or random access iterator which it keeps as a member which can be accessed through base() . To iterate backwards use rbegin() and rend() as the iterators for the end of the collection, and the start of the collection respectively.


1 Answers

Apparently the solution is what base() returns is 1 off. The following identity holds for a reverse_iterator:

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

Or in other words, the reverse_iterator is always one pass the regular iterator it is the base of. Not sure why.

In GCC

Simply change

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

to

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

I'm definitely curious though as to why the identity above makes sense.

In Visual Studio

Coming back into work and trying this in visual studio, I see the above solution doesn't quite work. The "nextIter" becomes invalid on the erase. Instead, you need to save away the temporary from the erase to get the next iterator instead of keeping around a nextIter like above.

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

So the final solution is

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;
        }
    }   

}

Note, associative containers do not return an iterator from erase. So this solution wouldn't work for map, multimap, etc.

like image 95
Doug T. Avatar answered Oct 20 '22 18:10

Doug T.