Search code examples
c++vectorloopsiteratorfor-loop

What's the cleanest way to walk and unwalk a std::vector using iterators?


I have a situation where I'm marching through a vector, doing things:

std::vector<T>::iterator iter = my_list.begin();

for ( ; iter != my_list.end(); ++iter )
{
  if ( iter->doStuff() )   // returns true if successful, false o/w
  {
    // Keep going...
  }
  else
  {
    for ( ; iter != m_list.begin(); --iter )  // ...This won't work...
    {
      iter->undoStuff();
    }
  }
}

Under normal conditions - assuming everything goes well - I march all the way to my_list.end() and end the loop successfully.

However, if something goes wrong while I'm doing stuff, I want to be able to undo everything - basically retrace my steps back to the very beginning of the vector, undoing everything one at a time in reverse order.

My problem is that when I get to my_list.begin() - as shown in the nested for loop - I'm really not done yet because I still need to call undoStuff() on my first element in the list. Now, I could just make the final call outside of the loop, but this seems a little unclean.

The way I see it, I'm only done when I get to my_list.rend(). However, I can't compare a std::vector::iterator to a std::vector::reverse_iterator.

Given what I'm trying to do, what's the best choice of iterator-type / loop combination?


Solution

  • While using reverse iterators via rbegin() and rend() works nicely, unfortunately I find that converting between reverse and non-reverse iterarotrs tends to be quite confusing. I can never remember without having to go through a logic-puzzle exercise whether I need to increment or decrement before or after the conversion. As a result I generally avoid the conversion.

    Here's the way I'd probably code your error handling loop. Note that I'd think that you wouldn't have to call undoStuff() for the iterator that failed - after all, doStuff() said it didn't succeed.

    // handle the situation where `doStuff() failed...
    
    // presumably you don't need to `undoStuff()` for the iterator that failed
    // if you do, I'd just add it right here before the loop:
    //
    //     iter->undoStuff();
    
    while (iter != m_list.begin()) {
        --iter;
        iter->undoStuff();
    }