Does modifying a std::vector invalidate an iterator? [duplicate] - c++

This question already has answers here:
Iterator invalidation rules for C++ containers
(6 answers)
Closed 2 years ago.
Consider this piece of code:
#include <vector>
#include <iostream>
int main(int argc, char** args)
{
std::vector<int> vec;
vec.push_back(0);
for (auto iter = vec.begin(); iter != vec.end(); iter++)
{
vec.push_back((*iter) + 1);
std::cout << *iter << std::endl;
}
}
I expected this to print all the numbers to Infinite. But what it did was to print a LOT of zeros (and an occasional -256, WHAT?) only to run into a segfault.
My assumtion is that iter still points to the old array after the call to vec.push_back moves the internal data to a new array.
What exactly is the problem here? Is my assumption correct, that the push_back call invalidates the iterator?
Interestingly, when I substitute std::vector for a std::list, the code works as expected. Is it save to use a std::list instead? Or is that just working correctly by accident?

std::vector::push_back invalidates all iterators on that vector if the new vector size is greater than the previous capacity. (The reason is due to the reallocation of the vector).
The behaviour of using an invalidated iterator is undefined.
std::list::push_back does not invalidate any iterators.

Related

Why cannot this simple vector c++ program work?

I am learning to use stl vector and It is odd that this program cannot work. What is wrong with it? How should I do if I want to implement the same function with vector?
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> vec;
vector<int>::iterator it;
vector<int>::iterator temp;
it = vec.begin();
vec.insert(it, -1);
it++;
vec.insert(it, 2);
for(temp = vec.begin();temp!=vec.end();temp++)
cout<<*temp<<' ';
return 0;
}
vec.insert(it, -1); invalidates it.
You should rather use it = vec.insert(it, -1); which will keep it valid.
You can see the documentation:
https://en.cppreference.com/w/cpp/container/vector
section called "Iterator invalidation" or look at this great question and answer: Iterator invalidation rules
On executing the code
vector<int> vec;
You created an object named vec, it has no elements and vec.size() will be zero.
So what vec.begin() returns is the same as what vec.end() returns.
By doing vec.insert(it, -1); you are inserting a value out of vec's range.
That is undefined behavior.
No, vec.insert(it, -1) works well, but vec.insert(it, -1) causes the vector to reallocate memory for its first element.
That invalids it.
Try vec.emplace_back(-1) or vec.resize(2) instead. They extend vec's size and capacity.

Does end iterator get update when inserting a new element? [duplicate]

This question already has answers here:
Iterator invalidation rules for C++ containers
(6 answers)
Closed 3 years ago.
I cannot figure why I'm getting an error when running the following simple program
#include <iostream>
#include <vector>
int main(int argc, char** argv) {
std::vector<int> v{ 1,2,3,4,5 };
std::vector<int>::iterator it1 = v.end();
auto it_tmp = v.insert(v.end(), 6);
std::vector<int>::iterator it2 = v.end();
std::cout << (it1 == it2) << std::endl;
return 0;
}
The iterators it1 and it2 are incompatible, so I was wondering what could possible be the issue. Iterators are incompatible if they belong to different containers, I would then assume in my case one of the two gets invalidated or something like that (I've also try to change v.end with v.begin() for both it1 and it2, it doesn't make any difference).
Thank you.
it1 == it2 evaluates false because after this auto it_tmp = v.insert(v.end(), 6);, end iterator changes.
std::vector::insert inserts before the given iterator. Everything before the insertion point remains valid. Everything after it is invalidated. it1 here is invalidated:
std::vector::insert
Causes reallocation if the new size() is greater than the old
capacity(). If the new size() is greater than capacity(), all
iterators and references are invalidated. Otherwise, only the
iterators and references before the insertion point remain valid. The
past-the-end iterator is also invalidated.

std::list insert() invalidates iterator? [duplicate]

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?

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.

C++ loop through map while erasing [duplicate]

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;
}