for (int i = 0; i < m_pGameField->dimensions()->first; i++)
{
for (int j = 0; j < m_pGameField->dimensions()->second; j++)
{
for (std::vector<std::shared_ptr<GameGridElement>>::iterator it = m_pGameField->getField(i, j)->getElements().begin();it != m_pGameField->getField(i, j)->getElements().end(); ++it)
{
if ((*it)->getName().compare("Spawn") == 0)
{
tmpSpawns.push_back(std::static_pointer_cast<SpawnElement>((*it)));
}
}
}
}
Hey Guys,
i have some problems with the upper code. The code snippet is supposed to find all the spawns in a 2d game field. WHen it comes to the head of the third for-iteration it spits out a Debug assertion error with the following message "Vector iterators incompatible. Now this are the values one step before the crash . I got them form the visual studio debugger:
getElements() returned {size = 0};i = 0;j = 0; begin() returned struct at null. it is struct at null
I see that the vector is empty, but shouldn't that be handled by the it != end() part?
You have not posted enough code to be really sure, but the following guess should be correct:
getElements()
most likely returns by value, not by reference.
For example:
std::vector<std::shared_ptr<GameGridElement>> Field::getElements()
{
// ...
return result;
}
This returns a copy! Which means that every time you call getElements
, you get a different vector.
For the compiler, this does not make any difference. Type safety does not help you, because a std::vector<std::shared_ptr<GameGridElement>>::iterator
is a std::vector<std::shared_ptr<GameGridElement>>::iterator
, even if you have two iterators which belong to different container instances. They have the same type, so the code compiles.
At run time, however, additional checks can be made to ensure that two iterators that are being compared really belong to the same container object. This turns seemingly random "strange" behaviour or spurious crashes into a definite guaranteed crash to immediately tell you that something must be fixed.
Consider again this line:
for (
std::vector<std::shared_ptr<GameGridElement>>::iterator
it = m_pGameField->getField(i, j)->getElements().begin();
it != m_pGameField->getField(i, j)->getElements().end();
++it
)
it
is initialised to getElements().begin()
and is then compared by !=
to getElements().end()
. But as explained above, each getElements()
returns a different vector, so you get incompatible iterators.
Here is an easy way to solve the problem:
std::vector<std::shared_ptr<GameGridElement>> elements =
m_pGameField->getField(i, j)->getElements();
for (std::vector<std::shared_ptr<GameGridElement>>::iterator it = elements.begin();
it != elements.end(); ++it)
This makes sure that begin()
and end()
return iterators to the same container instance.
For reference, here is a simple piece of code to reproduce the behaviour:
#include <vector>
int main()
{
std::vector<int> v1;
std::vector<int> v2;
bool b = v1.begin() == v2.begin();
}
Note that the C++ standard does not mandate iterator-compatibility checks at run time. Your compiler has to offer them and you must use the correct compiler options to enable them, or disable them if you don't need them or cannot afford them.
By the way, if you can use C++11, then you can use a range-based for
loop to not only fix the bug but also make the code a lot more concise.