This question already has an answer here:
Substraction or decrement random access iterator pointing to begin
(1 answer)
Closed 9 years ago.
What should std::map iterator decrement return, if there's only single element in the map? Here's the sample code
#include <map>
#include <stdio.h>
int main()
{
std::map<int, int> m;
m.insert(std::make_pair(1, 1));
//std::map<int, int>::iterator it = m.begin();
std::map<int, int>::iterator it = m.upper_bound(0);
printf("isbegin: %d\n", it == m.begin());
--it;
bool isend = it == m.end();
printf("isend: %d\n", isend);
}
On Windows it will print isend: 1, on Linux with g++ 4.6 it will print isend: 0.
The question: is the decrement above really a case of UB? and if not then what result is correct - Windows or Linux one?
UPDATE: modified code to show that upper_bound is called
Decrementing something to the element before begin() doesn't make sense. This is undefined behavior and there is no correct or incorrect answer.
for an iterator r the operation --r is valid if before the operation is done there exists s such that r == ++s and after the operation is done r is dereferenceable. (§24.2.6 Bidirectional.iterators )
Since begin() returns an iterator to the first element of the container there is no element s which can be incremented to get to r so this is undefined.
Related
This question already has answers here:
map.erase( map.end() )?
(4 answers)
Closed 2 years ago.
int main(){
map<int, int> m;
m.insert({1,2});
m.insert({2,3});
m.insert({5,10});
m.erase(m.find(3));
for(auto &x: m){
cout<<x.first<<" "<<x.second<<nl;
}
}
Output:
1 2
5 10
As far as I know m.find(3) returns iterator to the m.end() if key is not found. Then why pair {2,3} is deleted?
The pair is deleted because you violated a pre-condition of std::map::erase
iterator erase( const_iterator pos );
iterator erase( iterator pos );
The iterator pos must be valid and dereferenceable. Thus the end()
iterator (which is valid, but is not dereferenceable) cannot be used
as a value for pos.
Violating a pre-condition of a standard library function has undefined behavior. So deleting a seemingly random element is totally in line with that.
This question already has answers here:
How to remove from a map while iterating it?
(6 answers)
Closed 2 years ago.
I use this code to remove elements from map container that equal to some int.
for(auto x:m){
if((x.second)==element)m.erase(x.first);
}
As result Segmentation fault. I also tried this:
for(map<int,int>::iterator i=m.begin();i!=m.end();i++){
if((i->second)==element)m.erase(i);
}
Same result. If you put i++ into if/else program will freeze/loop or something. How can I fix this?
erase() invalidates the iterator being used by the for loop. Fortunately, erase() itself returns an iterator to the next entry, so the correct loop would look like this instead:
for (map<int,int>::iterator i = m.begin(); i != m.end(); )
{
if (i->second == element)
i = m.erase(i);
else
++i;
}
In addition to #john's answer, if your C++ Standard Library implementation supports it, you can invoke the std::erase_if(map, condition) helper:
std::erase_if(m, [](const auto& item) {
auto const& [key, value] = item;
// Write your erasing condition here, e.g.:
// return value == element;
});
This question already has answers here:
How to call erase with a reverse iterator
(13 answers)
Closed 3 years ago.
The community reviewed whether to reopen this question 6 months ago and left it closed:
Original close reason(s) were not resolved
I'm trying to solve a problem in C++, a part of which requires me to erase elements from a vector using the rbegin() member function. However, the compiler throws an error every time I write the below-mentioned code. What's wrong here?
int main() {
int a = 1, b = 2;
vector<int> V = {a, b};
auto it = V.rbegin();
V.erase(it);
return 0;
}
It compiles just fine, however, if I access the same element using the begin() member function. The code below works fine.
int main() {
int a = 1, b = 2;
vector<int> V = {a, b};
auto it = V.begin()+1;
V.erase(it);
return 0;
}
There is no std::vector::erase() overload for reverse_iterator. However, you can obtain the corresponding iterator from a reverse_iterator by calling its base() member function:
auto rit = V.rbegin();
auto it = rit.base();
V.erase(it);
This code does compile but results in undefined behavior because the iterator counterpart of rbegin() corresponds to end(). From std::vector::erase() documentation:
iterator erase(const_iterator pos);
The iterator pos must be valid and dereferenceable. Thus the end() iterator (which is valid, but is not dereferencable) cannot be used as a value for pos.
rbegin().base() returns end(), not end() - 1. Nevertheless, you can advance rbegin() by one if you want a dereferencable iterator:
auto it = (std::next(rit)).base();
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.
There have been a few questions regarding this issue before; my understanding is that calling std::vector::erase will only invalidate iterators which are at a position after the erased element. However, after erasing an element, is the iterator at that position still valid (provided, of course, that it doesn't point to end() after the erase)?
My understanding of how a vector would be implemented seems to suggest that the iterator is definitely usable, but I'm not entirely sure if it could lead to undefined behavior.
As an example of what I'm talking about, the following code removes all odd integers from a vector. Does this code cause undefined behavior?
typedef std::vector<int> vectype;
vectype vec;
for (int i = 0; i < 100; ++i) vec.push_back(i);
vectype::iterator it = vec.begin();
while (it != vec.end()) {
if (*it % 2 == 1) vec.erase(it);
else ++it;
}
The code runs fine on my machine, but that doesn't convince me that it's valid.
after erasing an element, is the iterator at that position still valid
No; all of the iterators at or after the iterator(s) passed to erase are invalidated.
However, erase returns a new iterator that points to the element immediately after the element(s) that were erased (or to the end if there is no such element). You can use this iterator to resume iteration.
Note that this particular method of removing odd elements is quite inefficient: each time you remove an element, all of the elements after it have to be moved one position to the left in the vector (this is O(n2)). You can accomplish this task much more efficiently using the erase-remove idiom (O(n)). You can create an is_odd predicate:
bool is_odd(int x) { return (x % 2) == 1; }
Then this can be passed to remove_if:
vec.erase(std::remove_if(vec.begin(), vec.end(), is_odd), vec.end());
Or:
class CIsOdd
{
public:
bool operator()(const int& x) { return (x % 2) == 1; }
};
vec.erase(std::remove_if(vec.begin(), vec.end(), CIsOdd()), vec.end());