C/C++ Arena

Step 6 of 7

Iterator invalidation

Changing a container can invalidate iterators, pointers and references into it. Using one afterwards is undefined behavior, and one of the most common C++ bugs in real code. The program might work, crash, or quietly corrupt data.

Container What invalidates
vector push_back that reallocates: everything. erase: the erased element and everything after it
deque inserting at either end invalidates iterators (but not references)
list, map, set only iterators to erased elements

The safe erase-while-looping pattern uses the iterator erase returns, which is the element after the removed one:

#include <iostream>
#include <map>
#include <string>

int main() {
    std::map<std::string, int> stock = {{"apples", 0}, {"bread", 4}, {"eggs", 0}, {"milk", 2}};
    int removed = 0;
    for (auto it = stock.begin(); it != stock.end(); ) {
        if (it->second == 0) {
            std::cout << "out of " << it->first << "\n";
            it = stock.erase(it);      // erase returns the next position
            removed++;
        } else {
            ++it;                      // only advance when nothing was erased
        }
    }
    std::cout << removed << " removed, left:";
    for (const auto& [item, n] : stock) std::cout << " " << item << "=" << n;
    std::cout << "\n";
}
out of apples
out of eggs
2 removed, left: bread=4 milk=2

Why the loop is shaped like this

Other traps to know

For vectors, std::erase_if(v, pred) does removal in one efficient call, as you saw in the algorithms module. The loop pattern is what you need for maps and for loops that do more than erase.

Your turn: write int remove_expired(std::map<std::string, int>& sessions, int now) that erases every session whose expiry time is <= now, returning how many were removed. Use the loop pattern.

Previous: Iterators Next: Challenge: callbacks with std::function