How to call erase with a reverse iterator using a for loop

After some use of this idiom, I think a modification to the loop in Jarod42's answer is in order to make things safer and maintain the typical for(;;) loop niceties:

for (auto it = testcont.rbegin(), nit = it; it != testcont.rend(); it = nit) {
    nit = next(it);

    // whatever... maybe a continue somewhere or maybe not

    if (WE_WANT_TO_ERASE(it)) {
        nit = decltype(it){ testcont.erase(std::next(it).base()) };
    }

    // whatever... maybe a continue somewhere or maybe not
}

Using the loop in the other answer is too dangerous. If one were to thoughtlessly add a continue; somewhere in the loop, without incrementing the iterator first, the result would be an infinite loop. Since, at first glace, this can look like a normal for(;;) loop, I believe that this is bound to happen sooner or later. Similarly, if there are branches in the loop, and if one of those branches neglects to increment the iterator, another bug is introduced. Finally, if you do an erase(), you need to be sure to continue before you increment the iterator or else you have yet another bug.

Using the modified loop above, the loop can be treated just like a normal for(;;) loop. The trick is to increment nit (the "next iterator") as the first line of the loop body. Then you don't have to worry. The only time you need to update nit is if you are doing an erase(). Everything else works as one would expect a for loop to work.

One final note: I originally asked the question with regard to maps, but this will work correctly for vector, list, etc, as well.


erase invalidates iterator, you have to reconstruct it from return of erase:

it = std::map<int,int>::reverse_iterator(testmap.erase( std::next(it).base() ));

or (c++11)

it = decltype(it){testmap.erase( std::next(it).base() )};

Demo.

For completeness, here is what the corrected loop from the original question looks like (notice that the iterator increment has been removed from the for(...):

for (auto rit = testmap.rbegin(); rit != testmap.rend(); /* empty */) {
    if (WE_WANT_TO_ERASE(rit)) {
        rit = decltype(rit){ testmap.erase(std::next(rit).base()) };
    } else {
        ++rit;
    }
}

Tags:

C++

C++11