Removing from std::list while iterating - c++

I have the following code:
bool resetTypeBit = true;
for (auto it = eventsList.begin(); it != eventsList.end(); ++it) {
CreatureEvent* curEvent = *it;
if (curEvent == event) {
it = eventsList.erase(it);
} else if (curEvent->getEventType() == type) {
resetTypeBit = false;
}
}
So I have the following scenario: eventList contains 01 item, and then, as soon as the for statement goes through for the first time and meet the it = eventsList.erase(it); line, the it variable becomes invalid, causing a segmentation fault on the next iteration of the for statement.
Any clues of what could be causing the problem?

If the item you remove is the last item in the list, the erase method will return end(). Your for loop will then try to increment that iterator, which results in undefined behaviour.
Another problem which you haven't come across yet is that, if the item you remove isn't the last item in the list, you'll end up skipping over the following item (because the iterator is incremented past the one that erase returns). You can think of erase as an increment operation that just happens to erase the item first.
The solution is to refactor the loop slightly, to move the increment to the end (and only if erase wasn't called):
bool resetTypeBit = true;
for (auto it = eventsList.begin(); it != eventsList.end(); ) {
CreatureEvent* curEvent = *it;
if (curEvent == event) {
it = eventsList.erase(it);
}
else {
if (curEvent->getEventType() == type) {
resetTypeBit = false;
}
++it; // move the increment to here
}
}

As it is written now, you are incrementing the iterator even in the erase branch, which means that you are always skipping the element just after an erased one. This is both incorrect and results in serious problems if the last element happens to be one to delete. To fix, you have to not increment if you already fix it by setting it to the element following the deleted one.
bool resetTypeBit = true;
for (auto it = eventsList.begin(); it != eventsList.end(); ) {
CreatureEvent* curEvent = *it;
if (curEvent == event) {
it = eventsList.erase(it);
continue;
} else if (curEvent->getEventType() == type) {
resetTypeBit = false;
}
++it;
}

Related

List iterator is not incrementable

I'm a bit rusty on c++ and am returning to it to get better. I searched a bit on StackOverflow already to no avail. The issue I am having is "list iterator not incrementable". Below is the section I believe to be the culprit, as this is the only location that I use an iterator with a modifier.
if (!io_queue.empty()) {
for (list<Process>::iterator it = io_queue.begin(); it != io_queue.end(); ++it) {
if (it->isBurstDone()) {
if (it->isComplete()) {
it->setExit(clock);
complete.push_back(*it);
it = io_queue.erase(it);
}
else {
ready_queue.push_back(*it);
it = io_queue.erase(it);
}
}
else {
it->decBurst();
}
}
}
You are incrementing it unconditionally on every loop iteration. But when erase() is called, it returns a new iterator to the next list element after the one being erased, and so if that results in it being set to the end iterator than incrementing it again is undefined behavior.
Simply move the ++it from the for statement to the else block inside the loop.
Try this:
if (!io_queue.empty()) {
list<Process>::iterator it = io_queue.begin();
do {
if (it->isBurstDone()) {
if (it->isComplete()) {
it->setExit(clock);
complete.push_back(*it);
}
else {
ready_queue.push_back(*it);
}
it = io_queue.erase(it);
}
else {
it->decBurst();
++it; // <— moved here
}
}
while (it != io_queue.end());
}

Removing list of itens from concurrent_unordered_map

I need to remove all items from my concurrent_unordered_map where the value is true
auto dataMap = new concurrency::concurrent_unordered_map<std::string, bool>();
(*dataMap)["1"] = false;
(*dataMap)["2"] = true;
(*dataMap)["3"] = false;
(*dataMap)["4"] = true;
(*dataMap)["5"] = false;
(*dataMap)["6"] = true;
I did a long search to see how to do this correctly but, I don't find some solution... my last try:
auto itr = dataMap->begin();
while (itr != dataMap->end())
{
auto data = (*itr);
if (data.second == true)
{
dataMap->unsafe_erase(data.first);
}
++itr;
}
So, how to remove the items, correctly ?
Edit:
I'm using VC++ 2019
You cannot use ++ on iterator to erased element. Accessing object which was deleted leads to undefined behaviour.
unsafe_erase returns the iterator to the first item past erased one (or end if erased was the last one).
So you can write:
while (itr != dataMap->end())
{
auto data = (*itr);
if (data.second == true)
itr = dataMap->unsafe_erase(data.first);
else
++itr;
}

Vector iterator invalidation

I am having an issue and I think it is because of the iterators being invalidated. However I use the iterator from erase() to resume iterating other the structure. When erase() when I try to increment after erase() is called the first time I get the following error
'vector iterator not incrementable '
std::map<uint32_t, std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>>::iterator itMap;
std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>::iterator itVector;
{
tvstd::lock_guard_mutex l(m_ConnectionsMutex);
itMap = m_Connections.find(static_cast<uint32_t>(pcp->ProcessID()));
if (itMap != m_Connections.end())
{
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); ++itVector)
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
}
}
}
}
Can anyone see where I am going wrong?
erase returns an iterator pointing to the new location of the element that followed the last element erased by the function call. This is the container end if the operation erased the last element in the sequence.
so if you erase you do not need to increment your iterator
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); )
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
}
else
{
++itVector
}
}
This solved my issue, I just have to call break after i erase but once i erase i do not need to loop to the end of the list. (#Aleexander solution also works)
std::map<uint32_t, std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>>::iterator itMap;
std::vector<std::pair<boost::uuids::uuid, tvshared::SecureIPCCallbackHandlePtr>>::iterator itVector;
{
tvstd::lock_guard_mutex l(m_ConnectionsMutex);
itMap = m_Connections.find(static_cast<uint32_t>(pcp->ProcessID()));
if (itMap != m_Connections.end())
{
for (itVector = itMap->second.begin(); itVector != itMap->second.end(); ++itVector)
{
if (commadUUID == itVector->first)
{
itVector->second.reset();
itVector = m_Connections[static_cast<uint32_t>(pcp->ProcessID())].erase(itVector);
break;
}
}
}
}

delete and erasing an iter?

i'm initializing and inserting into a list like so
_ARRAY_DETAIL* pAR = new _ARRAY_DETAIL;
pAR->sVar1 = 1;
pAR->nVar2 = 2;
m_SomeList.push_back(pAR);
i am trying to find and erase all from the list that contains the value 1, and then delete the pointer we created with new, is my example below doing both in a good, correct efficient way?
while(Iter != m_SomeList.end());
{
if((*Iter)->sVar1 == 1)
{
_ARRAY_DETAIL* pAR = *Iter;
Iter = m_SomeList.erase(Iter);
delete pAR; pAR = NULL;
}
Iter++;
}
Once you erase the iterator, it's no longer valid. You need to increment it before the erase.
if((*Iter)->sVar1 == 1)
{
_ARRAY_DETAIL* pAR = *Iter;
m_SomeList.erase(Iter++);
delete pAR;
}
else
++Iter;
You were correct that erase returns an incremented iterator but I prefer to do it explicitly, before the iterator is erased.
Setting pAR to NULL is redundant, since it's going out of scope on the next line anyway.
Also note that you should only increment Iter if you didn't increment it in the other part of the if.
as an alternative you could use remove if although what you have done seems fine.
bool IsOne (_ARRAY_DETAIL* pAR) {
if(pAR->sVar1 == 1) {
delete pAR;
return true;
}
return false;
}
remove_if (vec.begin(), vec.end(), IsOne);

Deleting a "value" from a multimap

My requirement is to delete a a "value" from the multimap and not the "key".
A key may have multiple values and i want delete a specific value.My requirement is similar to deleting a node from a linked list.
I am doing so by using multimap::erase() method.
But after deletion if I try to print the values of the multimap, the values deleted using multimap::erase() are also printed.
below is my code snippet:
void Clientqueues::clearSubscription(string name,string sessionid)
{
pair<multimap<string,string>::iterator,multimap<string,string>::iterator> i;
multimap<string, string>::iterator j;
i = registeredClientInfo.equal_range(name);
if (j == registeredClientInfo.end())
return;
for(j=i.first;j != i.second;++j)
{
if((j->second) == sessionid) registeredClientInfo.erase(j->second);
}
for(j=i.first;j != i.second;++j)
{
cout<<""<<j->second<<endl;///This prints the erased values too;
}
}
Am i doing something wrong?
Any help in this regard greatly appreciated.
Most important, you call erase(j->second), when you meant to call erase(j). You're not erasing the element of the multimap pointed to by j, you're erasing all elements whose keys are equal to the value of the element pointed to by j (which is sessionid). I expect that's nothing.
Also: call equal_range again after the erase loop is complete - the effect of using an erased iterator is undefined, so if you erased the first iterator i.first, then you can't start iterating from there again afterwards.
Note that this also means there's a bug in your loop that does the erase, since in the case that you do call erase, you increment j when it holds an iterator value that's no longer valid. Unfortunately, the correct code is:
for(j=i.first;j != i.second;)
{
if((j->second) == sessionid) {
auto next = j;
++next;
registeredClientInfo.erase(j);
j = next;
} else {
++j;
}
}
Or if you prefer:
for(j=i.first;j != i.second;)
{
auto current = j;
++j;
if((current->second) == sessionid) registeredClientInfo.erase(current);
}
Or if the entry is unique for the key/value pair, so that you only have to remove at most one thing, then:
for(j=i.first;j != i.second;++j)
{
if((j->second) == sessionid) {
registeredClientInfo.erase(j);
break;
}
}
if (j == registeredClientInfo.end()) return; isn't right either, since j is uninitialized when you do it. If the key isn't found, then equal_range returns an empty range (two equal iterator values), so your other loops will do nothing anyway.
If you deleted i.first or i.second the iterators get invalidated implying undefined behavior.