Adding an element to a Vector while iterating over it
Asked Answered
M

3

17

As the title says, I want to add an element to a std::vector in certain cases while iterating through the vector. With the following code, I'm getting an error "Debug assertion failed". Is it possible to achieve what I want to do?

This is the code I have tested:

#include <vector>

class MyClass
{
public:
    MyClass(char t_name)
    {
        name = t_name;
    }
    ~MyClass()
    {
    }
    char name;
};

int main()
{
    std::vector<MyClass> myVector;
    myVector.push_back(MyClass('1'));
    myVector.push_back(MyClass('2'));
    myVector.push_back(MyClass('3'));

    for each (MyClass t_class in myVector)
    {
        if (t_class.name == '2')
             myVector.push_back(MyClass('4'));
    }
    return 0;
}

EDIT:

Well, I thought for each was standard C++, but it seems that it's a Visual Studio feature:

for each, in

Visual c++ "for each" portability

Misjudge answered 18/6, 2016 at 18:5 Comment(4)
for each (MyClass t_class in myVector) is this c++ ??Aarika
That's not valid c++ code!Stratigraphy
Never mind the iffy syntax, it doesn't make sense to insert elements into a vector while you're iterating it, because push_back may invalidate all existing iterators.Adscription
@formerlyknownas_463035818 #define in : now it isUnseal
C
25

The act of adding or removing an item from a std::vector invalidates existing iterators. So you cannot use any kind of loop that relies on iterators, such as for each, in, range-based for, std::for_each(), etc. You will have to loop using indexes instead, eg:

int main()
{
    std::vector<MyClass> myVector;

    myVector.push_back('1');
    myVector.push_back('2');
    myVector.push_back('3');

    std::vector<MyClass>::size_type size = myVector.size();
    for (std::vector<MyClass>::size_type i = 0; i < size; ++i)
    {
        if (myVector[i].name == '2')
        {
             myVector.push_back('4');
             ++size; // <-- remove this if you want to stop when you reach the new items
        }
    }

    return 0;
}
Coefficient answered 18/6, 2016 at 20:47 Comment(3)
why have a variable for the size? i < myVector.size() works tooLoth
@Loth Doing so forces a function call to size() on every iteration, whereas when using a variable, there is only 1 function call to size() before the loop is entered. If size() is inlined, it likely doesn't really matter either way, but if size() is not inlined then performance may be affected if the vector size is large. More importantly, your approach doesn't allow the loop to stop when the end of the old items is reached, knowing that none of the new items will match the condition being tested for.Coefficient
makes sense, thanks for the clarificationLoth
A
6

As pointed out by pyon, inserting elements into a vector while iterating over it (via iterators) doesnt work, because iterators get invalidated by inserting elements. However, it seems like you only want to push elements at the back of the vector. This can be done without using iterators but you should be careful with the stop condition:

std::vector<MyClass> myVector;
size_t old_size = myVector.size();
for  (int i=0;i<old_size;i++) {
    if (myVector[i].name == '2') { myVector.push_back(MyClass('4')); }
}
Aarika answered 18/6, 2016 at 18:19 Comment(0)
B
0

After following the previous answers, you can use const auto& or auto& to have clean code. Should be optimized in release build by the compiler.

std::vector<MyClass> myVector;
std::vector<MyClass>::size_type size = myVector.size();
for (std::vector<MyClass>::size_type i = 0; i < size; ++i)
{
    const auto& element = myVector[i];
    element.do_stuff();
}
Badly answered 9/3, 2022 at 14:38 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.