One of the most frequent errors that occur in my code is that STL containers are modified during a loop.
Elements are removed or added during a loop execution so I usually run into out of bounds exceptions.
My for loops usually looks like this:
for (auto& Item : Items) { // Will not work when Items container is modified
//... loop logic
}
When multiple items can be removed, I use this monstrosity:
for (int Index=Items.size()-1;Index<=0;Index--) {
if (Index<Items.size()) { //Because multiple items can be removed in a single loop
//... loop logic
}
}
This looks bad and it makes me feel bad using that second option. The reason multiple items can be removed is due to events, where a single event can remove any number of elements.
Here is some pseudo code to illustrate when this occurs:
// for each button in vector<button> {
// process button events
// event adds more buttons to vector<button>
// *ERROR* vector<button> is modified during loop.
// }
In another example, imagine a vector with following items:
// 0 1 2 3 4 5 6 7 8 9
We start our loop at 0 and go element by element. At 4, I want to remove elements 1,4 and 9 so we can't use a normal loop here.
Use std::remove_if with a predicate that decide if a button needs to be removed:
bool needsRemoved(const Button& button);
vec.erase(std::remove_if(vec.begin(), vec.end(), &needsRemoved), vec.end());
EDIT: For your last example, the quadratic (i.e. bad for performance) algorithm is:
std::vector<int> vec = {0,1,2,3,4,5,6,7,8,9};
auto end = vec.end();
for (auto it = vec.begin(); it < end; ++it)
{
std::set<int> bad = {1, 4, 9};
end = std::remove_if
(vec.begin(), end,
[bad](int x) { return (bad.find(x) != bad.end()); });
}
vec.erase(end, vec.end());
You will probably be better off using a container with fast lookup though (like a set, or a map).
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With