Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

a small issue with std::vector and changing the collection while looping through it

Tags:

c++

stdvector

This loop changes the iterators while running:

std::vector<int> c;
c.push_back(1);
c.push_back(2);

std::vector<int>::iterator iter    = c.begin();
std::vector<int>::iterator endIter = c.end();

while( iter != endIter )
{
    std::cout << (*iter) << std::endl;
    iter = c.erase(iter);
}

It does not work because:

Iterators and references to the erased elements and to the elements between them and the end of the container are invalidated. Past-the-end iterator is also invalidated

How can I rewrite this (without using std::list, and using the while loop) ?

By the way, I know that auto has been implemented since C++11. Why would it be beneficial to use it ?

like image 654
Athanase Avatar asked Jun 17 '13 17:06

Athanase


4 Answers

Simply do not cache the end iterator that will be invalidated:

while( iter != c.end() )
{
    std::cout << (*iter) << std::endl;
    iter = c.erase(iter);
}

or clear the vector after printing:

for(const auto& i : c) {
    std::cout << i << std::endl;
}
c.clear();
like image 125
Casey Avatar answered Nov 02 '22 19:11

Casey


Erasing an element changes end(). Change the loop:

while( iter != c.end())
like image 36
Beta Avatar answered Nov 02 '22 18:11

Beta


Either

  • Rewrite it as

    while( iter != c.end() )
    {
        std::cout << (*iter) << std::endl;
        iter = c.erase(iter);
    }
    

    and the code will no longer rely on any potentially invalidated iterators,

or

  • "Refresh" any potentially invalidated iterators after each invalidating operation

    while( iter != endIter )
    {
        std::cout << (*iter) << std::endl;
        iter = c.erase(iter);
        endIter = c.end();
    }
    

These are the two generic approaches typically used in cases like that.

like image 7
AnT Avatar answered Nov 02 '22 20:11

AnT


A more idiomatic way of doing this...

while(c.begin() != c.end()) c.erase(c.begin());

Though this is very slow, as a vectors underlying implementation uses a contiguous array(with extra space on the end). So repeatedly erasing the begin element is very ineficient, as every element ends up getting copied one space in the array earlier, n - index times! You can jurastically increase performance by doing this:

while(c.begin() != c.end()) c.pop_back();
like image 3
ChrisCM Avatar answered Nov 02 '22 18:11

ChrisCM