Search code examples
c++iteratorerase

Why no operator+ for std::list iterators?


I was about to write code like this:

std::list<whatevertype> mylist;

// ...

std::list<whatevertype>::iterator it;

for(it = mylist.begin(); it != mylist.end(); ++it) {
    // ...
    if(some condition)
        mylist.erase(it);
}

But I realized, this code is wrong: mylist.erase(x) will invalidate the iterator it, so the ++it is likely to fail.

So I tried changing it to

std::list<whatevertype>::iterator it;
std::list<whatevertype>::iterator nextit;

for(it = mylist.begin(); it != mylist.end(); it = nextit) {
    // ...
    nextit = it + 1;
    if(some condition)
        mylist.erase(it);
}

But, to my surprise, this failed: evidently operator+ is not defined for std::list iterators.

I've since found this other question and learned that the standard idiom for deleting "out from under" an iterator is more like

for(it = mylist.begin(); it != mylist.end(); ) {
    if(some condition)
          it = mylist.erase(it);
    else  ++it;
}

I believe I could also get away with

for(it = mylist.begin(); it != mylist.end(); ) {
    // ...
    std::list<whatevertype>::iterator previt = it;
    ++it;

    if(some condition)
        mylist.erase(previt);
}

But my question is, is there a reason that operator+ is not defined for these iterators?


Solution

  • One rule they had with the std iterators and collection was to make expensive things verbose.

    On a list iterator, it+50 takes O(50) time. On a vector iterator, it+50 takes O(1) time. So they implemented + on vector iterators (and other random access iterators) but not on list iterators (and other weaker iterators).

    std::next and std::advance and std::prev can solve your problem easier:

    auto previt = std::prev(it);
    

    or

    auto nextit = std::next(it);
    

    these also take a count, but because they are an explicit function call it was decided that them being expensive is acceptable.

    Among other things, you can search for calls to std::next and std::prev and get iterator manipulation; + is heavily overloaded and finding the expensive calls is hard.

    Note that std::basic_string doesn't follow the same conventions as other std containers.