Debugging challengeIntermediate

Erasing while iterating

Scenario

A cache-eviction routine removes every expired entry from an unordered_map and every non-positive value from a vector. Under AddressSanitizer both loops report heap-use-after-free; without it the program sometimes skips elements and sometimes segfaults. Find the bug in each loop.

1#include <iostream>
2#include <unordered_map>
3#include <vector>
4#include <string>
5using namespace std;
6
7void evictExpired(unordered_map<string, int>& ttl, int now) {
8 for (auto it = ttl.begin(); it != ttl.end(); ++it) {
9 if (it->second <= now)
10 ttl.erase(it); // invalidates it, then ++it
11 }
12}
13
14void removeNonPositive(vector<int>& v) {
15 for (size_t i = 0; i < v.size(); ++i) {
16 if (v[i] <= 0)
17 v.erase(v.begin() + i); // shifts the next element into slot i
18 }
19}
20
21int main() {
22 unordered_map<string, int> ttl = {{"a", 1}, {"b", 5}, {"c", 2}};
23 evictExpired(ttl, 3);
24 vector<int> v = {3, 0, 0, -1, 4};
25 removeNonPositive(v);
26 for (int x : v) cout << x << ' '; // expected: 3 4
27}

Your task

  1. For the unordered_map loop, explain what the standard says about an iterator after erase(it), and why ++it afterwards is undefined behaviour.
  2. For the vector loop, trace v = {3, 0, 0, -1, 4} by hand and show which element is skipped.
  3. Fix both loops. For the vector, give two fixes: one that keeps the index loop and one that is O(n).
  4. Which containers guarantee that erasing one element does not invalidate iterators to the others?
  5. State the complexity of each fixed loop.
DebuggingImplementationSystematic Reasoning

Work it out

Write your analysis before revealing anything. The self-check below compares it against what a strong answer contains.

Reveal

Progressive — each section builds on the previous one.

The bug
Why it happens
The fix
Edge cases
Complexity

Self-check

Tick what your analysis covered. Be honest — this feeds your readiness profile.

0/6

Related concepts