Removing item from vector, while in C++11 range 'for' loop?
I have a vector of IInventory*, and I am looping through the list using C++11 range for, to do stuff with each one.
After doing some stuff with one, I may want to remove it from the list and delete the object. I know I can call delete
on the pointer any time to clean it up, but what is the proper way to remove it from the vector, while in the range for
loop? And if I remove it from the list will my loop be invalidated?
std::vector<IInventory*> inv;
inv.push_back(new Foo());
inv.push_back(new Bar());
for (IInventory* index : inv)
{
// Do some stuff
// OK, I decided I need to remove this object from 'inv'...
}
No, you can't. Range-based for
is for when you need to access each element of a container once.
You should use the normal for
loop or one of its cousins if you need to modify the container as you go along, access an element more than once, or otherwise iterate in a non-linear fashion through the container.
For example:
auto i = std::begin(inv);
while (i != std::end(inv)) {
// Do some stuff
if (blah)
i = inv.erase(i);
else
++i;
}
Every time an element is removed from the vector, you must assume the iterators at or after the erased element are no longer valid, because each of the elements succeeding the erased element are moved.
A range-based for-loop is just syntactic sugar for "normal" loop using iterators, so the above applies.
That being said, you could simply:
inv.erase(
std::remove_if(
inv.begin(),
inv.end(),
[](IInventory* element) -> bool {
// Do "some stuff", then return true if element should be removed.
return true;
}
),
inv.end()
);
You ideally shouldn't modify the vector while iterating over it. Use the erase-remove idiom. If you do, you're likely to encounter a few issues. Since in a vector
an erase
invalidates all iterators beginning with the element being erased upto the end()
you will need to make sure that your iterators remain valid by using:
for (MyVector::iterator b = v.begin(); b != v.end();) {
if (foo) {
b = v.erase( b ); // reseat iterator to a valid value post-erase
else {
++b;
}
}
Note, that you need the b != v.end()
test as-is. If you try to optimize it as follows:
for (MyVector::iterator b = v.begin(), e = v.end(); b != e;)
you will run into UB since your e
is invalidated after the first erase
call.
Is it a strict requirement to remove elements while in that loop? Otherwise you could set the pointers you want to delete to NULL and make another pass over the vector to remove all NULL pointers.
std::vector<IInventory*> inv;
inv.push_back( new Foo() );
inv.push_back( new Bar() );
for ( IInventory* &index : inv )
{
// do some stuff
// ok I decided I need to remove this object from inv...?
if (do_delete_index)
{
delete index;
index = NULL;
}
}
std::remove(inv.begin(), inv.end(), NULL);
sorry for necroposting and also sorry if my c++ expertise gets in the way of my answer, but if you trying to iterate through each item and make possible changes (like erasing an index), try using a backwords for loop.
for(int x=vector.getsize(); x>0; x--){
//do stuff
//erase index x
}
when erasing index x, the next loop will be for the item "in front of" the last iteration. i really hope this helped someone