I’m tracking a bug and I run into very strange behaviour. I have set of pointers and when I erase them one-by-one, first one erases, but erasing another gives me segfault.
I use
size_type erase( const key_type& key );
so it can’t be something with iterators. My debuger shows me that in callstack:
0 - std::less<cSubscriber *>::operator() //cSubscriber is an abstract base class and I have a set of cSubscriber *
1 - std::_Rb_tree<cSubscriber*, cSubscriber*, std::_Identity<cSubscriber*>, std::less<cSubscriber*>, std::allocator<cSubscriber*> >::equal_range
2 - std::_Rb_tree<cSubscriber*, cSubscriber*, std::_Identity<cSubscriber*>, std::less<cSubscriber*>, std::allocator<cSubscriber*> >::erase
3 - std::set<cSubscriber*, std::less<cSubscriber*>, std::allocator<cSubscriber*> >::erase
4 - cEventSystem::unsubscribe //my function, it is as follows in the class which has the set as its member
cEventSystem::unsubscribe(cSubscriber * ptr)
{
set.erase(ptr);
}
And in the base cSubscriber abstract class there is virtual destructor:
virtual ~cSubscriber()
{
eventSystem.unsubscribe(this);
}
Any ideas? I have no idea how can it cause segfault, the erase should just return 0 when there isn’t such an element. Or maybe it crashesh when trying to erase something from empty container? (I have another bug when after adding 3 differents pointers the size of set is only 2 but that’s another story).
If you pass a invalid address to your
std::set<SOMETHING*>::erase()it will segfault when trying to compare the passed value with what is in the container.For example:
Update
based on comments
Since you are not redefining the default comparator, and the default comparator does not dereference your pointers, then the only way is that your
std::setis corrupted.Internally,
std::setis implemented as a binary tree. This mean that it has a lot of pointers in the way to find a value and to erase it. Ifstd::setis corrupted some of theses pointers will point to a invalid memory address. This invalid memory address will be used to pass a reference (reference of a pointer,cSubscriber* & const) of the compared value tostd::less.std::lessreceives a reference to a pointer and is going to dereference the reference to get the pointer value.This way, a invalid memory inside the
std::setdid only show up atstd::less, because thestd::setdid not actually touch the invalid memory, it gave the invalid memory address to our poor fellastd::lessthat opened it and got a segfault in it’s face.My point with all that is that if you create a comparator that uses copies instead of reference, the corruption will show up inside the
std::setwhen it tries to copy the pointer value to give to the comparator.