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.
Related
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:
How to invalidate an iterator?
(4 answers)
What is iterator invalidation?
(3 answers)
Closed 4 years ago.
Say I have a simple program as so:
int main(void) {
std::list<int> l;
auto it = l.begin();
auto it2 = l.insert(l.begin(), 5);
std::cout << (it == it2) << std::endl;
}
Doesn't this show that the iterator it has been invalidated by inserting into the list. Yet the C++ standard says that insertion into a list does not invalidate iterators.
Originally it would probably hold a nullptr since the list was empty. Now it no longer points to any iterator part of the list. So is it not invalidated?
This question already has answers here:
What happens if you call erase() on a map element while iterating from begin to end?
(3 answers)
Closed 9 years ago.
To loop through a map in c++ we do sth like this
map<string,int> mymap;
map<string,int>::iterator it= mymap.begin();
while(it!=mymap.end()) {
//code here
it++;
}
What if in the "code here" part i have an if statement that if evaluated to true, it erases one element from the map? How should my code change so that it still loops through all mymap elements in order?
http://en.cppreference.com/w/cpp/container/map/erase :
References and iterators to the erased elements are invalidated. Other
references and iterators are not affected.
(So make sure you increment and save a "next" iterator before you erase.
Edit: In fact since C++11, erase returns the next iterator anyway, so you may use that.)
you may want to reassign your iterator when you erase an element, as it wont be valid otherwise...
it = mymap.erase(...)
To avoid using the iterator after invalidating it when erasing, the loop body should be like this:
if (should_erase) {
it = my_map.erase(it); // C++11: returns the next iterator
my_map.erase(it++); // Historic C++: no helpful return value
} else {
++it;
}
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.
This question already has answers here:
What happens if you call erase() on a map element while iterating from begin to end?
(3 answers)
Closed 8 years ago.
Can I continue to use an multimap iterator even after a call to multimap::erase()? For example:
Blah::iterator iter;
for ( iter = mm.begin();
iter != mm.end();
iter ++ )
{
if ( iter->second == something )
{
mm.erase( iter );
}
}
Should this be expected to run correctly, or is the iterator invalidated following the call to erase? Reference sites like http://www.cplusplus.com/reference/stl/multimap/erase.html are strangely quiet on this topic of the lifespans of iterators, or the effects of constructive/destructive methods on iterators.
http://www.sgi.com/tech/stl/Multimap.html
Multimap has the important property that inserting a new element
into a multimap does not invalidate iterators that point to existing
elements. Erasing an element from a multimap also does not invalidate
any iterators, except, of course, for iterators that actually point to
the element that is being erased.
So it should look like this:
Blah::iterator iter;
for ( iter = mm.begin();iter != mm.end();)
{
if ( iter->second == something )
{
mm.erase( iter++ );
// Use post increment. This increments the iterator but
// returns a copy of the original iterator to be used by
// the erase method
}
else
{
++iter; // Use Pre Increment for efficiency.
}
}
Also see:
What happens if you call erase() on a map element while iterating from begin to end?
and
delete a specific entry in the map,but the iterator must point to the next element after the deletion
C++ Standard 23.1.2.8:
The insert members shall not affect the validity of iterators and references to the container, and the erase members shall
invalidate only iterators and references to the erased elements.
This is a common requirement for all associative containers, and std::multimap is one of them.