C++ - Why is program crashing with if statement inside map iterator? - c++

I am new to C++ and I am trying to iterate through the map while passing an if statement through. However the program crashes.
Please help me fix the program.
#include <bits/stdc++.h>
#include <iostream>
#include <set>
#include <string>
#include <iterator>
using namespace std;
int main()
{
std::map<int,int> h;
std::map<int,int>::iterator it;
h[1] = 2;
h[4] = 5;
for(it = h.begin(); it !=h.end(); it++){
if (it->second > 4){
h.erase(it->first);
}
}

You're erasing element inside the for loop, and the iterator pointing to the removed element (i.e. it) will be invalidated. Then it++ will cause problem.
You could
for (it = h.begin(); it != h.end(); ) {
if (it->second > 4){
it = h.erase(it); // set it to iterator following the last removed element
} else {
++it;
}
}

Your iterator removes the element the iterator is pointing to.
When an element in a std::map is removed, all iterators to its are immediately invalidated.
The iterator is no longer valid after the element is removed. Afterwards, the for-loop attempts to increment the no-longer-valid iterator. This is why your code crashes.
The typical solution goes something like this:
for(it = h.begin(); it !=h.end(); ){
{
std::map<int,int>::iterator p=it;
++it;
if (p->second > 4){
h.erase(p->first);
}
}
Note, that the element is removed only after the iterator is already incremented.

You are blowing away the iterator. It becomes invalid as soon as you remove an element from the map.
Also, you you might want to change your iterator increment to ++it. Can provide a little speed boost.

Related

How can I erase elements from a std::set in reverse (from the end until a lower_bounds)

So I need to erase elements from a std::set in a particular order, doing something with the first.
so if I had a set containing {1,2,3,4,5,6} and my I wanted to go until 4, I need to:
doSomething(6);
erase(6);
doSomething(5);
erase(5);
doSomething(4);
erase(4);
I have the following code that does not work:
#include <iostream>
#include <set>
void doSomething(int value) {
std::cout << value << '\n';
}
int main() {
std::set<int> s = {1,2,3,4,5,6};
auto beginIt = s.end();
auto endIt = s.lower_bound(4);
auto rbegin = std::make_reverse_iterator(beginIt);
auto rend = std::make_reverse_iterator(endIt);
for (auto it = rbegin; it != rend;) {
doSomething(*it);
s.erase(std::next(it).base());
}
return 0;
}
I think the issue is that it erasing the end iterator then keeps going util it crashes.
How can I get this to work.
godbolt: https://godbolt.org/z/KvaGWhr4G
The correct way of doing what you want is to getting your end iterator each time.
for (auto it = rbegin; it != std::make_reverse_iterator(s.lower_bound(4));) {
doSomething(*it);
s.erase(std::next(it).base());
}
Now let's see why you initial code didn't work.
In set, the iterators are not invalidated after erasing an element, EXCEPT for the iterator that was pointing to the erased element.
Now let's see what happened in the last iteration when you remove 4.
When dereferencing the rend, we will see that it points to 3. However, the base of rend points to 4. And after removal of 4, the base of rend has been invalidated. So your program had Undefined behavior.
To understand why getting end iterator at every iteration works, we have to understand that during the program the base of it is always s.end(). And at the last step, when we call s.lower_bound(4), we get s.end(). Hence, the condition for exiting the loop is satisfied.

Erase all even numbers from vector using find_if [duplicate]

This question already has answers here:
Iterator invalidation rules for C++ containers
(6 answers)
Closed 5 years ago.
#include <iostream>
#include <vector>
#include <algorithm>
#include <time.h>
#include <iomanip>
using namespace std;
bool isEven(int n)
{
return n%2 == 0;
}
int main()
{
srand(time(NULL));
vector<int> myVec;
for(int i = 0; i < 20; i++)
{
myVec.push_back(rand() % 100);
}
while(1)
{
vector<int>::iterator q = std::find_if(myVec.begin(), myVec.end(), isEven);
cout << *q << endl;
if(q == myVec.end())
{
myVec.erase(q);
break;
}
else
myVec.erase(q);
}
return 0;
}
This code is giving segmentation fault. The above code is to remove all the even numbers from the vector using find_if and erase function
Please help. Any help will be highly appreciated.
EDIT: I have edited it to make sure that iterator will be valid always.
Still it is giving segmentation fault.
std::vector::erase invalidates all iterators to and after the point of erasure. You can't continue using that iterator, not to increment it, use it to access the vector, or even compare it to end().
The correct algorithm to use is std:remove_if. Unlike the name implies, it will only move all even items of the vector "to the back", without invalidating any iterators. It will return an iterator to the start of this sub-range, which you can then just feed to the appropriate erase overload (the one that accepts a pair of iterators).
This has been used so much in code that it's even named "the erase-remove idiom".
When using the erase(it); function the iterator changes so you need to set the iterator again to the new one returned by the erase function.
In your code, you are checking for the end if(q == myVec.end()) and then using erase this will throw an error as.end() does not point to data, and to be able to erase an item from the vector the iterator needs to be valid. So by changing if(q == myVec.end()) to if(q == (myVec.end()-1)) it will allow you to delete the last element in case of been a pair.

How to erase element with a reverse iterator from a `std::set`?

Facing issue with deletion of last element in set :
#include <bits/stdc++.h>
using namespace std;
int main()
{
set < pair <int,int > > a;
a.insert(make_pair(2,3));
auto it = a.rbegin();
a.erase(it.base()); // for deleting last element in set
cout << a.size() << endl;
return 0;
}
Getting the runtime issue, Also have tried with auto, Iterator and Const iterator, it's not working.Is there any other way to erase an element from a set ?
Edit : How can I delete a Particular element based on iterator reference ?
if i do like :
auto it=a.begin();
a.erase(it); Here it = reference to the element for deletion
it doesn't work.Any other way to delete based on iterator reference ?
Is there any other way to erase an element from a set ?
a.erase(std::prev(std::end(a)));
Can you tell me,whats problem with my code ?
itr.base() where itr == a.begin() is equivalent to a.end(). See: http://en.cppreference.com/w/cpp/iterator/reverse_iterator/base
Erasing a past-the-end iterator is undefined behavior.

Using std::deque::iterator (in C++ STL) for searching and deleting certain elements

I have encountered a problem invoking the following code:
#include<deque>
using namespace std;
deque<int> deq = {0,1,2,3,4,5,6,7,8};
for(auto it = deq.begin(); it != deq.end(); it++){
if(*it%2 == 0)
deq.erase(it);
}
which resulted in a segmentation fault. After looking into the problem I found that the problem resides in the way the STL manages iterators for deques: if the element being erased is closer to the end of the deque, the iterator used to point to the erased element will now point to the NEXT element, but not the previous element as vector::iterator does. I understand that modifying the loop condition from it != deq.end() to it < deq.end() could possibly solve the problem, but I just wonder if there is a way to traverse & erase certain element in a deque in the "standard form" so that the code can be compatible to other container types as well.
http://en.cppreference.com/w/cpp/container/deque/erase
All iterators and references are invalidated [...]
Return value : iterator following the last removed element.
This is a common pattern when removing elements from an STL container inside a loop:
for (auto i = c.begin(); i != c.end() ; /*NOTE: no incrementation of the iterator here*/) {
if (condition)
i = c.erase(i); // erase returns the next iterator
else
++i; // otherwise increment it by yourself
}
Or as chris mentioned you could just use std::remove_if.
To use the erase-remove idiom, you'd do something like:
deq.erase(std::remove_if(deq.begin(),
deq.end(),
[](int i) { return i%2 == 0; }),
deq.end());
Be sure to #include <algorithm> to make std::remove_if available.

vector iterators incompatible while erase from vector

I have a map which elements are vectors.I have to delete from these vectors all elements which are equal to special number num
std::map<size_t,std::vector<size_t> > myMap;
for (std::map<size_t,std::vector<size_t> >::iterator itMap = myMap.begin();itMap != myMap.end();++itMap )
{
for (std::vector<size_t>::iterator itVec = itMap->second.begin();itVec != itMap->second.end();)
{
auto itNextVec = itVec;
++itNextVec;
if (*itVec == num)
{
itMap->second.erase(itVec );
}
itVec = itNextVec;
}
}
The code causes run-time exepssion .In VS - vector iterators incompatible.
Can someone point what is the cause for that?
Thanks
std::vector::erase returns an iterator to the next position of the list, and so when you do an erase you should make your iterator equal to the returned value.
The only thing that you have to consider is that the returned iterator could be the end so you should check for that.
What I personally like to do is is after doing in an erase and I get the next iterator position, I go back to the previous position of the returned iterator and than call a continue on the for loop
Example:
#include <vector>
#include <iostream>
int main()
{
std::vector<int> myInt;
myInt.push_back(1);myInt.push_back(2);myInt.push_back(3);
for(auto iter = myInt.begin();
iter != myInt.end();
++iter)
{
if(*iter == 1)
{
iter = myInt.erase(iter);
if(iter != myInt.begin())
{
iter = std::prev(iter);
continue;
}
}
std::cout << *iter << std::endl;
}
}
But doing an erase inside of a iterator loop is frowned upon because it invalidates the old iterator and that could cause a lot of issues if you didn't plan for them.
erasing will invalidate the iterator
Iterator validity
Iterators, pointers and references pointing to position (or first) and beyond are
invalidated, with all iterators, pointers and references to elements before position (or
first) are guaranteed to keep referring to the same elements they were referring to
before the call.
You can't trivially erase an item from a collection while iterating over it. Think a little about it, your removing what itVec "points" to, after the removal itVec no longer "points" to an element, so it no longer have a "next" pointer.
If you check e.g. this reference, you will see that the erase function returns an iterator to the next element. Continue the loop with this one (without increasing it of course).
Consider either using a different collection class than vector or creating a new vector with the desired items removed rather than removing from existing vector.