0

Alright so, I'm in the middle of a loop that iterates all objects in a vector:

for (auto &itr : m_entities) {
    itr.second->Update(l_time);
    if (itr.second->m_isDead) 
        Remove(itr.first); //don't worry, it does not remove immediately so the vector size wont change 
    }

m_entities is a vector that processes derived objects inherited from a base class, and the loop simply calls the Update() function of each object in that vector. Simple enough, and everything was working fine.

However, when I decided to call the Add() function, which simply adds a new object to the vector, I get an exception:

_Myval2 was 0xDDDDDDDD

I know the problem: I am calling the Add() function inside of the Update() function in one of the derived classes, which changes the size of the vector while still doing the iteration. Like so:

for (auto &itr : m_entities) {
    itr.second->Update(l_time); //here is where I call "Add," changing the size of the vector  
    if (itr.second->m_isDead)   //and messing everything up
        Remove(itr.first);
}

So my question is: How can I add to the vector INSIDE of the loop and still be able to finish the loop without getting an error?

PS: I need to add it inside of the loop. The feature that I want doesn't work if I do like I did with the Remove() function. The Remove() function only pushes the ID of the entity to a vector that will be later deleted from the vector. Yes, it will work if I do something like that with the Add() function, but as soon as I add the entity inside the loop, I want to modify it:

if (sf::Mouse::isButtonPressed(sf::Mouse::Left)) {
    if (...) {
        ...

        //Here it will add to the vector and return the entity ID, which 
        //I use to find the entity and modify it right away
        unsigned int bulletId = m_entityMgr->Add(EntityType::Bullet);

        //just a pointer to the newly added entity so I can modify it
        Bullet *bullet = (Bullet*)m_entityMgr->Find(bulletId);

        ... //modifying entity
    }
}

And then right after it exits from this Update() function, it goes back to that loop and the error pops up because the size of the vector was modified.

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
  • You can't do anything that changes the number of elements in a container while using range-based `for`. You [invalidate the iterators](https://stackoverflow.com/questions/6438086/iterator-invalidation-rules) it's using as book-keeping. Some containers are more forgiving than others, but this is one case where `vector` is less-than-friendly. – user4581301 Dec 07 '20 at 23:36
  • Is there a solution for this without invalidating it? – Pedro Sarmento Dec 07 '20 at 23:37
  • 1
    Can't you just store those pointers resulting from `Add/Find` and process them afterwards (like you do with `Remove`)? – bloody Dec 07 '20 at 23:37
  • Tactical note on the remove code: [Look up `std::remove_if`](https://en.cppreference.com/w/cpp/algorithm/remove). You should be able to get it to work for you. – user4581301 Dec 07 '20 at 23:40

1 Answers1

1

In a range-based for loop, you can't do anything that modifies the size of the container. In this case, since you are only adding elements, simply use a traditional index-based for loop instead, eg:

for (size_t i = 0; i < m_entities.size(); ++i)
{
    auto &itr = m_entities[i];
    itr.second->Update(l_time); // may increase the size of the vector
    if (itr.second->m_isDead)
        Remove(itr.first);
}

Or, if you don't want to call Update() on new items added:

size_t size = m_entities.size();
for (size_t i = 0; i < size; ++i)
{
    auto &itr = m_entities[i];
    itr.second->Update(l_time); // may increase the size of the vector
    if (itr.second->m_isDead)
        Remove(itr.first);
}
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770