set<int> s = {1, 2, 3, 4};
auto it = s.begin();
while (it != s.end()) {
// this correct
s.erase(it++);
// this incorrect
s.erase(it);
it++;
}
why on top of code can running?
My understand of the order when my code running is:
When the erase function is executed, then iterator was deleted.
The chaotic iterators executing add, it behaviour is undefined.
But it running normal, So my problem is why it can running and these code have difference?
The best way would be:
it = s.erase(it);
Your code with post-increment also works, but it is less transparent. To recall, post-increment version is semantically equivalent with the following snippet:
temp = it;
++it;
s.erase(temp);
As for your claims of ill-formed (second version) code "running", the code which has undefined behavior can be "running" or even seem to deliver expected results. This is the gist of undefined behavior. And the code which increments iterator which has been erased exhibits undefined behavior, as per std::set::erase documentation:
References and iterators to the erased elements are invalidated. Other
references and iterators are not affected
Related
Consider the following code
std::set<int> int_set = {1, 2, 3, 4};
for(const auto& key : int_set)
{
if(key == 2)
{
int_set.erase(key);
break;
}
}
The code runs as expected, but is it safe?
It feels wrong to be using a reference to a key to erase itself from a set, as presumably once the erase has happened the reference is no longer valid.
Another code snippet with the same potential problem would be
std::set<int> int_set = {1, 2, 3, 4};
const auto& key = *int_set.find(2);
int_set.erase(k);
This is safe in the code provided since you break out of the loop without attempting to use the reference (key) again (nor implicitly advance the underlying iterator that for-each loops are implemented in terms of, which would happen if you did not break/return/throw/exit()/crash, when you looped back to the top of the loop) after the call to erase. key itself is only needed to find the element to remove; until that element is removed, it's valid, once it's removed, it's not used again by erase (it already found the element to erase, there's no possible use for it after that point).
If you tried to use key after the erase, you'd be using a dangling reference, invoking undefined behavior. Similarly, even allowing the loop to continue (implicitly advancing the underlying iterator) would be illegal; the iterator is invalid, and the implicit advancing of the iterator when you returned to the top of the loop would be equally invalid. The safe way to erase more than one element as you iterate would be to switch from for-each loops (that are convenient, but inflexible) to using iterators directly, so you can update them with the return value of erase, e.g.:
for(auto it = int_set.begin(); it != int_set.end(); /* Don't increment here */)
{
if (predicate(*it)) {
it = int_set.erase(it); // In C++11 and higher, erase returns an iterator to the
// element following the erased element so we can
// seamlessly continue processing
} else {
++it; // Increment if we didn't erase anything
}
}
Of course, as noted in the comments, if you only need to remove one element with a known value, the whole loop is pointless, being a slow (O(n) loop + O(log n) erase) way to spell:
int_set.erase(2); // Returns 1 if 2 was in the set, 0 otherwise
which is a single O(log n) operation.
I have a sample program in which I am trying to see how the iterator invalidates while deleting the elements from a map.
The program is here:
#include <iostream>
#include <map>
using namespace std;
int main(int argc, char *argv[])
{
map<int, int> myMap;
myMap.insert(pair<int, int>(0, 2));
myMap.insert(pair<int, int>(1, 4));
myMap.insert(pair<int, int>(3, 18));
myMap.insert(pair<int, int>(2, 20));
map<int, int>::iterator it;
for(it = myMap.begin(); it != myMap.end(); ++it)
{
myMap.erase(it); // erasing the element pointed at by iterator
cout << it->first << endl; // iterator is invalid here
}
return 0;
}
The problem is that I am getting output is:
0
1
2
3
Why the iterator is not invalidating and giving me wrong results. Any help would be highly appreciated.
Documentation of C++ STL maps says that: References and iterators to
the erased elements are invalidated. Other references and iterators
are not affected.
Using an invalidated iterator is undefined behaviour. In such case, anything could happen.
Why do you see the values? The iterator contains a pointer to some piece of memory, by pure accident, this memory has not yet been returned to the system and has not yet been overwritten. This is why you still can see the already "dead" values.
It does not change anything, it remains undefined behaviour, and the next time you run the program, the memory page the map element resided in could already have been returned to the OS again and you get an access violation (segmentation fault)...
Invalidated iterator does not mean that its internal data was erased. Sometimes like in this case the invalidated iterator may hold a valid reference to the next item. However, using it like this is Undefined Behavior and it likely to cause some problems in your application.
There are no run-time checks for invalid iterators by default.
You can enable the debug checks for invalid iterators with -D_GLIBCXX_DEBUG for GNU C++ standard library. That produces the following run-time error:
iterator "this" # 0x0x7fff9f3d7060 {
type = N11__gnu_debug14_Safe_iteratorISt17_Rb_tree_iteratorISt4pairIKiiEENSt7__debug3mapIiiSt4lessIiESaIS4_EEEEE (mutable iterator);
state = singular;
references sequence with type `NSt7__debug3mapIiiSt4lessIiESaISt4pairIKiiEEEE' # 0x0x7fff9f3d7150
}
For other standard libraries check the documentation.
I used poll() with std::vector.
registed listen socket.
std::vector<struct pollfd> fds;
fds.push_back(server_sock);
and add new client socket or connected client session do something.
// poll() ...
for(std::vector<struct pollfd>::reverse_iterator it = fds.rbegin(); it != fds.rend(); it++) {
if (it->fd == server_sock) {
struct pollfd newFd;
newFd.fd = newClient;
newFd.events = POLLIN;
fds.push_back(newFd);
} else {
// do something.
}
}
but the reverse_iterator does not work properly when there is a 1 or 2 or 4 vector's element. I don't understand why this work.
attached sample code.
typedef struct tt_a {
int a;
short b;
short c;
} t_a;
vector<t_a> vec;
for (int i = 0; i < 1; i++) {
t_a t;
t.a = i;
t.b = i;
t.c = i;
vec.push_back(t);
}
for(vector<t_a>::reverse_iterator it = vec.rbegin(); it != vec.rend(); it++) {
if (it->a == 0) {
t_a t;
t.a = 13;
t.b = 13;
t.c = 13;
vec.push_back(t);
}
printf("[&(*it):0x%08X][it->a:%d][&(*vec.rend()):0x%08X]\n",
&(*it), it->a, &(*vec.rend()));
}
printf("---------------------------------------------\n");
for(vector<t_a>::reverse_iterator it = vec.rbegin(); it != vec.rend(); ++it) {
if (it->a == 3) {
it->a = 33;
it->b = 33;
it->c = 33;
}
printf("[&(*it):0x%08X][it->a:%d][&(*vec.rend()):0x%08X]\n",
&(*it), it->a, &(*vec.rend()));
}
result:
[&(*it):0x01ADC010][it->a:0][&(*vec.rend()):0x01ADC028]
[&(*it):0x01ADC008][it->a:33][&(*vec.rend()):0x01ADC028]
[&(*it):0x01ADC000][it->a:0][&(*vec.rend()):0x01ADC048]
If vector has 5 elements, it works normally.
[&(*it):0x007620A0][it->a:4][&(*vec.rend()):0x00762078]
[&(*it):0x00762098][it->a:3][&(*vec.rend()):0x00762078]
[&(*it):0x00762090][it->a:2][&(*vec.rend()):0x00762078]
[&(*it):0x00762088][it->a:1][&(*vec.rend()):0x00762078]
[&(*it):0x00762080][it->a:0][&(*vec.rend()):0x00762078]
---------------------------------------------
[&(*it):0x007620A8][it->a:13][&(*vec.rend()):0x00762078]
[&(*it):0x007620A0][it->a:4][&(*vec.rend()):0x00762078]
[&(*it):0x00762098][it->a:33][&(*vec.rend()):0x00762078]
[&(*it):0x00762090][it->a:2][&(*vec.rend()):0x00762078]
[&(*it):0x00762088][it->a:1][&(*vec.rend()):0x00762078]
[&(*it):0x00762080][it->a:0][&(*vec.rend()):0x00762078]
push_back invalidates iterators when it causes size to exceed capacity:
If the new size() is greater than capacity() then all iterators and references (including the past-the-end iterator) are invalidated. Otherwise only the past-the-end iterator is invalidated.
Basically, if you must push_back, make sure to reserve ahead of time so you don't invalidate your iterator.
Your program most likely has crashed. You are manipulating the container while still iterating over it.
[&(*it):0x01ADC008][it->a:33][&(*vec.rend()):0x01ADC028]
You can see junk '33' while it should be '13'.
And why are you even trying to dereference the end iterator
&(*vec.rend())
This basically will be a junk irrespective of the vector size. Its an undefined behavior and will crash your application randomly.
As shadow points out fix the vector size before iterating, but still I am not sure how that will fix your code as your example has other issues that will cause seg fault
For normal (forward, not reverse) vector iterators, inserting into the vector invalidates any iterators that point to anywhere at or after the point of insertion. Furthermore, if the vector must be resized, all iterators are invalidated.
This alone could explain your problems, as because you have not reserved space in your vector (by calling vec.reserve(SIZE)), any of your push_back calls could trigger a resize and invalidate your iterators, which will result in undefined behaviour when you try to use them afterwards.
However, reverse iterators are more complicated, and the same guarantee does not hold for reverse iterators, and I believe any insertion may invalidate them.
Internally, a reverse iterator holds a forwards iterator to the element after the one that it points to. When dereferenced, the reverse iterator decrements this forwards iterator and returns its dereferenced value. So rbegin() internally has a copy of end(), and rend() has a copy of begin(). The above rules for forward iterator invalidation then imply that at the very least, a reverse iterator will be invalidated if an insertion occurs at any point up to one element after the location of the reverse iterator. So if you have an iterator pointing to index 0 in a length 1 vector, push_back will insert to index 1, which will invalidate the iterator. If you then continue to use that iterator (such as when dereferencing it in the subsequent printf call) then you will have undefined behaviour.
Undefined behaviour means anything could happen, and very commonly different systems will produce different behaviour. Do not assume that just because this code runs as expected on your system with an initial vector size of 5 that it will work on other systems. Any code invoking undefined behaviour is inherently fragile, and should be avoided.
For me (running Visual Studio 2015), I get a crash at the printf line regardless of the size of the vector. If I call vec.reserve(10) to eliminate the resizing-invalidation issue, then it only crashes when vec is initially of length one.
Additionally, you are dereferencing vec.rend() in your printf arguments, which is also undefined behaviour, even if you are just trying to get an address out of it. (I had to comment out this to get your code to run, otherwise it would crash every time even without the push_back call.)
Is it allowed to increment an iterator variable it that already is at end(), i.e. auto it = v.end()?
Is it allowed in general?
If not, is it not allowed for vector?
If yes, is ++it maybe idempotent if it==v.end()?
I ask, because I stumbled upon code like this:
std::vector<int> v{ 1, 2, 3, 4, 5, 6, 7 };
// delete every other element
for(auto it=v.begin(); it<v.end(); ++it) { // it<end ok? ++it ok on end?
it = v.erase(it);
}
It works fine with g++-6, but that is no proof.
For one it<v.end() may only work with vectors, I suppose it should read it!=v.end() in general. But in this example that would not recognize the end of v if ++it is applied when it already is on the end.
No the behaviour is undefined. You are allowed to set an iterator to end(), but you must not increment it or dereference it.
You are allowed to decrement it so long as the backing container is not empty.
I was reading about the std::deque container and the document states that
Insertion and deletion of elements in std::deque may invalidate all
its iterators
Here is my version of understanding of the above statement kindly let me know if I am misinterpreting the statement or missing something
Consider the following code
std::deque<int> s;
s.push_back(12);
auto i = s.begin();
s.push_front(45);//After pushing 45 at the back now `i` may be invalid.
Is this understanding correct ?
You are correct. For example after
std::deque<int> s;
s.push_back(12);
auto i = s.begin();
s.push_front(45)
calling *i is undefined behavior.