Strange/interesting behaviour with std::vector iterators and resizing/reserving - c++

I have two very similar bits of code; this:
std::vector<int> fail{0};
fail.reserve(2);
std::vector<int>::iterator it1 = fail.begin(), it2 = fail.begin() + 1;
fail.push_back(0);
it1 == it2;
which throws a "vector iterators incompatible" exception and this:
std::vector<int> fail{0, 0};
fail.reserve(3);
std::vector<int>::iterator it1 = fail.begin(), it2 = fail.begin() + 1;
fail.push_back(0);
it1 == it2;
which doesn't. It seems to be due to the it2 being the end of the vector in the first example but not in the second, but I'd just like to get a full clarification for why the first throws but the second doesn't.
For reference I am using MSVC.

std::vector::push_back always invalidates the past-the-end iterator, so in the first case it2. This happens regardless of resizing.
All other iterators stay intact if the vector does not reallocate, that's why they second snippet is fine.

Related

How to use erase and iterator to delete a item in the two-dimension vector?

A two-dimension vector: (I want to go through vector and if I find the same name as "and", then delete or erase it, but why it can only work with it1 not it2? how to fix it?)
vector<Animals>numberOfAnimals(12);
vector<vector<Animals>>all(4,numberOfAnimals);
void delete_item(vector<vector<Animals>>&all,string ans)
{
for (auto it1=all.begin();it1!=all.end();it1++)
{
for (auto it2=(*it1).begin();it2!=(*it1).end();it2++)
{
if ((*it2).getter_name()==ans)
{
all.erase(it1); //no error
all.erase(it2--); //error
}
}
}
}
all is a vector of vectors. it1 is an iterator to all, so it can be passed to all.erase. it1 refers to one of the vectors inside all.
it2 is not an iterator to all, so it cannot be passed to all.erase. it2 is an iterator to the vector *it1 which is a vector of Animals. it2 can be passed to it1->erase. it2 refers to one of the Animals in *it1.
However, note that erasing an iterator to vector invalidates it. it1 and it2 are still used in the loop in a way that is not allowed to be done on an invalid iterator. Thus using erase in the loop without fixing the iterator has undefined behaviour.
The idiomatic way to selectively erase some elements of a container is erase-remove:
auto predicate = [&ans](const Animals& a) {
return a.getter_name()==ans;
};
for(std::vector<Animals>& v : all) {
v.erase(std::remove_if(v.begin(), v.end(), predicate));
}
This removes all of the Animals that match the predicate in each of the vectors within all.

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.

c++ std::list compile issue? related to list.end() and list.end() - 1

In below code.
int main() {
list<int> m_list;
m_list.push_back(1);
list<int>::iterator it1 = (--m_list.end()); // it works, *it1 return 1;
list<int>::iterator it2 = (m_list.end() - 1); // compile issue?
}
Anybody explain why in list (m_list.end() - 1) has compile issue? and why (--m_list.end()) is OK?
If we change to others, vector, string. both cases do work.
int main() {
vector<int> m_vector;
m_vector.push_back(1);
vector<int>::iterator it1 = (--m_vector.end()); // both work
vector<int>::iterator it2 = (m_vector.end() - 1); // both work
}
The reason behind this is that list::end() returns a bidirectional iterator which does not support such operation.
Source:
http://www.cplusplus.com/reference/iterator/BidirectionalIterator/
On the other hand, vector::end() and string::end() returns a random access iterator which supports such operation.
http://www.cplusplus.com/reference/iterator/RandomAccessIterator/
Edit:
If you really want to accomplish the task, use std::prev() function
list<int>::iterator it2 = (std::prev(m_list.end(), 1));
As suggested by Pete Becker, "The second argument to std::prev has a default of 1"
So, you may do this also:
list<int>::iterator it2 = (std::prev(m_list.end()));
Anybody explain why in list (m_list.end() - 1) has compile issue?
Because list iterator doesn't support random access. Only random access iterators are guaranteed to support operator- (and operator+).
and why (--m_list.end()) is OK?
Because bidirectional iterators support operator-- (and operator++). List iterator is bidirectional.
If we change to others, vector, string. both cases do work.
Both vector and string have random access iterators.

Point to previous value without decrementing pointer

This is a pretty simple question.
Basically, say I have two iterators, it1 and it2. Given a value for it1, I want to define it2 to point to a location one address earlier. It would be cool if I could do it in one line, like:
vector<int>::iterator it2 = --it1;
However, this simultaneously decrements it1, so I have to re-increment it1.
vector<int>::iterator it2 = --it1;
++it1;
If these two lines are involved in a performance-intensive loop, I will have lots of it1 going back and forth for no good reason, just to define it2. On the other hand, if I do:
vector<int>::iterator it2 = it1;
--it2;
This is also slightly less than optimal as it involves two steps. Is there a way to do it in one?
You're looking for std::prev:
vector<int>::iterator it2 = std::prev(it1);
For vector's iterator, pointers and random access iterator in general, you can also use operator -:
vector<int>::iterator it2 = it - 1;

Assign value of one iterator into another iterator C++

Say I have an iterator it which is pointing to some element of map.
Also I have another iterator it1 , and I want to do something like this
it1 = it + 1;
How can we achieve this in C++ as above statement gives error in C++.
In C++11, you say auto it1 = std::next(it, 1);.
Prior to that, you have to say something like:
std::map<K, T>::iterator it1 = it;
std::advance(it1, 1);
Don't forget to #include <iterator>.