I know that erasing elements from an associative container inside a for loop invalidates it.
Is it the case when using a range based loop?
#include <iostream>
#include <unordered_map>
#include <set>
#include <algorithm>
#include <ranges>
struct A {
int value;
// other members...
};
int main() {
std::unordered_map<std::string, std::set<A>> myMap;
// Assuming you have populated the map and the set
// Object of type A
A a;
for (auto& [key, valueSet] : myMap) {
valueSet.erase(a);
if (valueSet.empty()) {
myMap.erase(key);
}
}
return 0;
}
And how can the compiler detects it.
I run -fsanitize=address,undefined
but gcc doesn't show any warning.
Is it the case when using a range based loop?
Generally yes, a range-based for loop is just using iterators under the hood. It effectively expands to:
{ // for (init-statement; range-declaration : range-expression) { ... }
/* init-statement */
auto && __range = /* range-expression */;
auto __begin = /* begin-expr */;
auto __end = /* end-expr */;
for ( ; __begin != __end; ++__begin) {
/* range-declaration */ = *__begin;
/* ... */
}
}
Any operation which invalidates iterators might also interfere with a range-based for loop as a result.
std::unordered_map::erase
invalidates the iterator to the erased element. In your case, erase(key)
is unsafe, because it invalidates the __begin
iterator which is used under the hood. ++__begin
would then advance an invalidated iterator, which is undefined behavior.
-fsanitize=address,undefined
does catch this type of mistake. (See live example at Compiler Explorer). It is a heap-use-after-free
. Are you sure that -fsanitize
is even supported on your platform? Maybe it doesn't do anything because you're using MinGW and an old version of GCC.