For unordered_map (and every hash table in the known universe) erasing anything invalidates all iterators, so you can't iterate while erasing. For std::map, you can if you're very, very careful (erasing invalidates the iterator you're currently on, but if you cache the next iterator, THEN erase the current one, it'll probably work, but be very fiddly). Most languages forbid this entirely: e.g. Rust's ownership model doesn't allow it, Python throws an exception, etc. It's just a very bad idea in general.
for(decltype(cont)::const_iterator it=cont.begin();it!=cont.end();++it){
if(Keep(it.first)){
++it;
}else{
it=cont.erase(it);
}
}
There's an argument to be made that maybe you should do something else, but if you want to do the above, you can!EDIT: just saw your example and checked cppreference, it says the return value "Iterator following the last removed element" for std::unordered_map. So i think you need to add an `it--` after your erase, otherwise it will "skip over" the next element. Right?
Also just read this little nugget on cppreference for unordered_map::erase:
> Removes specified elements from the container.The order of the remaining elements is preserved. (This makes it possible to erase individual elements while iterating through the container.)
This seems like a crazy guarantee to put in the standard, it must really limit the kinds of hash tables you can make that matches the unordered_map interface.
(In the map/unordered_map/set/etc. case, you can get away with storing std::advance(it) and then erasing, but when forward-iterating vectors the iterator invalidation rules are quite different and the API is designed to cover this.)
It’s a great and useful guarantee.
> it must really limit the kinds of hash tables you can make that matches the unordered_map interface.
Many libraries treat containers as “abstract” with many possible implementations. STL explicitly does not. It’s a specific data structure from a computer science class.
In reality, very few real-life containers can support this pattern, which is why this is a headline case for Rust, because it statically prevents this bug.
But yes, for removal the correct thing is always to use `std::erase_if` (C++) or `retain()` (Rust). For insertions, the only real solution is to build up a separate collection while iterating and then merging it into the original container when done. Yucky, but won't crash.
The STL containers are full of "features" that containers in other languages just do not support, yet it's worse to use in my opinion.