I have a strange problem that rarely happens relating to invalidated STL iterators that I've simplified in the example code below.
Foo.h
#include "Bar.h"
#include <map>
class Foo
{
std::map<int, Bar*> test;
public:
Foo() {}
void Update();
void AddEntry(int i, Bar* bar);
void DeleteEntry(int i);
};
Foo.cpp
#include "Foo.h"
void Foo::Update() {
for(auto iter = test.rbegin(); iter != test.rend(); iter++) {
iter->second->DoThingOne();
iter->second->DoThingTwo(); // BREAKS ON 2nd ITERATION
}
}
void Foo::AddEntry(int i, Bar* b) {
test[i] = b;
}
void Foo::DeleteEntry(int i) {
delete test[i];
test.erase(i);
}
Bar.h
class Foo;
class Bar
{
Foo* f;
static int count;
public:
friend class Foo;
Bar(Foo* f_);
void DoThingOne();
void DoThingTwo();
};
Bar.cpp
#include "Bar.h"
#include "Foo.h"
int Bar::count = 0;
Bar::Bar(Foo* f_) : f(f_) {}
void Bar::DoThingOne() {
if(count++ == 1) {
f->DeleteEntry(3);
}
}
void Bar::DoThingTwo() {
// Does things
}
Main.cpp
#include "Foo.h"
int main() {
Foo* foo = new Foo();
Bar* one = new Bar(foo);
Bar* two = new Bar(foo);
Bar* three = new Bar(foo);
foo->AddEntry(1, one);
foo->AddEntry(2, two);
foo->AddEntry(3, three);
foo->Update();
return 0;
}
So basically, when Foo::Update
is called, the 1st iteration of the loop proceed normally, then the 2nd iteration calls DoThingOne which deletes the entry in the map that the previous iteration of the loop just used. When DoThingTwo
is called right after I get a "Debug Assertion Failed! Expression: map/set iterator not decrementable" error crashing the program.
From what I understand, iterators over maps and sets are always valid except for iterators referring to elements removed, but here the iterator is referring to the element right after the one removed. My only guess is that it has something to do with the element being removed being the first/last element and the iterator in use is then referring to the new first/last element, but I still can't find out exactly why this is happening or how to work around it. I only really have the option of detecting when this happens in the for-loop before DoThingTwo is called and trying to fix it there.
Edit: After looking at the link provided by Nemo, I changed the loop to the following and it seems to work:
void Foo::Update {
auto iter = test.end();
for(iter--; iter != test.begin(); iter--) {
iter->second->DoThingOne();
iter->second->DoThingTwo();
}
iter->second->DoThingOne();
iter->second->DoThingTwo();
}
It looks really sloppy, but this does get the job done. Since using iterator instead of reverse_iterator works, I guess it does have something to do with how reverse_iterator works compared to iterator.