I have a list list<x> bar and an array of iterators list<x>::iterator foo[100] initialized all elements with bar.end().
I want to store pointers in foo to some elements in bar while I'm going to erase, insert and push_back elements into bar. For element insertion I have to check if a position in foo is empty or not:
if (foo[my_number] != bar.end())
//do something 1
else
//do something 2
I receive an error only in case the foo[my_number] is already pointing to a bar element. The if is true I got this error (if false, no problem):
Debug Assertion Failed!
Program: C:\Windows\system32\MSVCP120D.dll
File: C:\Program Files (x86)\Microsoft Visual Studio 12.0\VC\include\list
Line: 289
Expression: list iterators incompatible
I read here that after insertion the bar.end() is not the same as before. However, my code doesn't fail if I push back elements and compare a bar.end and foo[k] = end. But fails to compare bar.end to foo[k] = bar.begin() + p (this bar element exist).
What am I missing? How can I overcome this problem? Should I re-actualize foo after I inserted elements?
insert or push_back operation in std::list does not invalidate iterators or references. erase invalidates iterators (and references) to the erased elements. The problem is caused by that you use iterator to erased element, after erase (or before), you should update this element iterator also in your array - preferable to end iterator.
The problem is that you should first find it, so before erase you might need to search it. How to you plan to keep synchronized your array and list?
Below is some example what you can and should not do:
std::list<int> lst;
std::vector<std::list<int>::iterator> arr;
// Init arr with end() iterator
arr.push_back(lst.end());
// Add new list element
lst.push_back(0);
// Remember where it is in arr
arr[0] = lst.begin();
// Compare it.
if ( arr[0] == lst.begin())
{
std::cout << "this is OK!\n";
}
lst.push_back(0);
if (arr[0] == lst.begin())
{
std::cout << "this is still OK!\n";
}
lst.erase(lst.begin());
//arr[0] = lst.end(); - without this, "list iterators incompatible" is issued by VS below
if (arr[0] == lst.begin())
{
// list iterators incompatible
std::cout << "This is WRONG!\n";
}
Erasing an element from a std::list invalidates iterators pointing to it (see here). So if you do erase from bar, be sure to remove the corresponding iterators from foo. All other std::list operations are supposed to keep existing iterators valid.
The link you found points to a question about std::vector which behaves differently with regard to iterator invalidation.
Related
Why does the following code crash? And what should I do when I am iterating via reverse iterator. How do I erase individual elements then?
deque q;
q.push_back(4);
q.push_back(41);
q.push_back(14);
for (auto it = q.begin(); it != q.end();) {
auto current = it;
q.erase(current);
it++;
}
Why does the following code crash ? How do I erase individual elements then ?
std::deque::erase invalidates iterators.
All iterators and references are invalidated, unless the erased elements are at the end or the beginning of the container, in which case only the iterators and references to the erased elements are invalidated.
The past-the-end iterator is also invalidated unless the erased elements are at the beginning of the container and the last element is not erased.
In your code, the iterators to the element to be erased (i.e. it and current) will become invalid after q.erase(current), then it++ will lead to UB.
You could make use of the return value of std::deque::erase
Iterator following the last removed element. If the iterator pos refers to the last element, the end() iterator is returned.
for (auto it = q.begin(); it!=q.end(); )
{
it = q.erase(it);
}
And what should I do if I am iterating via reverse iterator.
Because std::deque::erase doesn't accept reverse_iterator as parameters, you need to use base() to convert it to normal iterator (pay attention to the position conversion). Such as
for (auto it = q.rbegin(); it!=q.rend(); )
{
it = std::make_reverse_iterator(q.erase((++it).base()));
}
As per C++11 23.3.3.4 deque modifiers /4, deque iterators become invalid if you delete certain elements.
An erase operation that erases the last element of a deque invalidates only the past-the-end iterator and all iterators and references to the erased elements.
An erase operation that erases the first element of a deque but not the last element invalidates only the erased elements.
An erase operation that erases neither the first element nor the last element of a deque invalidates the past-the-end iterator and all iterators and references to all the elements of the deque.
In your case, you're usually only ever erasing the first element so it will only invalidate that element. That means the it++ is invalid and you should instead use something like:
it = q.erase(it);
inside the loop, since the erase call itself returns an "adjusted" iterator. This will also work when removing the last element.
However, since your code is totally clearing the list (assuming it's not a cut down version of something which needs to process each element), you can ditch the loop altogether and just use:
q.clear();
As the other answerers have already pointed out, erasing elements from the queue will invalidate the iterators you are using to iterate its elements. Thus it fails.
But I assume that you don't intend to erase all elements in the queue, in which case you probably would have rather used:
q.erase(q.begin(), q.end());
or
q.clear();
Therefore, I'd like to suggest using another technique, that can be used to delete items matching certain criteria from a queue: the erase-remove idiom.
Here, the functions std::remove(...) and std::remove_if(...) are used to move the items to be deleted (matching certain criteria) to the end of the container. The range-based version of q.erase(...) is then used to delete the items.
Here's an example:
#include <deque>
#include <algorithm>
#include <iostream>
// predicate function for removal of elements
bool greater_three(int x) {
return x > 3;
}
int main() {
std::deque<int> q = {1,2,3,4,5};
for (auto i : q) std::cout << i << " "; std::cout << "\n";
// delete all items with value 3
q.erase(std::remove(q.begin(), q.end(), 3), q.end());
for (auto i : q) std::cout << i << " "; std::cout << "\n";
// delete all items with value > 3
q.erase(std::remove_if(q.begin(), q.end(), greater_three), q.end());
for (auto i : q) std::cout << i << " "; std::cout << "\n";
}
The output is:
$ g++ test.cc -std=c++11 && ./a.out
1 2 3 4 5
1 2 4 5
1 2
For reference:
http://en.cppreference.com/w/cpp/container/deque/erase
http://en.cppreference.com/w/cpp/container/deque/clear
http://en.cppreference.com/w/cpp/algorithm/remove
q clearly doesn't support removing elements while iterating through them.
I have a map which elements are vectors.I have to delete from these vectors all elements which are equal to special number num
std::map<size_t,std::vector<size_t> > myMap;
for (std::map<size_t,std::vector<size_t> >::iterator itMap = myMap.begin();itMap != myMap.end();++itMap )
{
for (std::vector<size_t>::iterator itVec = itMap->second.begin();itVec != itMap->second.end();)
{
auto itNextVec = itVec;
++itNextVec;
if (*itVec == num)
{
itMap->second.erase(itVec );
}
itVec = itNextVec;
}
}
The code causes run-time exepssion .In VS - vector iterators incompatible.
Can someone point what is the cause for that?
Thanks
std::vector::erase returns an iterator to the next position of the list, and so when you do an erase you should make your iterator equal to the returned value.
The only thing that you have to consider is that the returned iterator could be the end so you should check for that.
What I personally like to do is is after doing in an erase and I get the next iterator position, I go back to the previous position of the returned iterator and than call a continue on the for loop
Example:
#include <vector>
#include <iostream>
int main()
{
std::vector<int> myInt;
myInt.push_back(1);myInt.push_back(2);myInt.push_back(3);
for(auto iter = myInt.begin();
iter != myInt.end();
++iter)
{
if(*iter == 1)
{
iter = myInt.erase(iter);
if(iter != myInt.begin())
{
iter = std::prev(iter);
continue;
}
}
std::cout << *iter << std::endl;
}
}
But doing an erase inside of a iterator loop is frowned upon because it invalidates the old iterator and that could cause a lot of issues if you didn't plan for them.
erasing will invalidate the iterator
Iterator validity
Iterators, pointers and references pointing to position (or first) and beyond are
invalidated, with all iterators, pointers and references to elements before position (or
first) are guaranteed to keep referring to the same elements they were referring to
before the call.
You can't trivially erase an item from a collection while iterating over it. Think a little about it, your removing what itVec "points" to, after the removal itVec no longer "points" to an element, so it no longer have a "next" pointer.
If you check e.g. this reference, you will see that the erase function returns an iterator to the next element. Continue the loop with this one (without increasing it of course).
Consider either using a different collection class than vector or creating a new vector with the desired items removed rather than removing from existing vector.
// Erase the missing items
vector<AlignedFDRData>::size_type StandardNum = FDRFreq.at(0).fData.size();
vector<AlignedFDRData>::iterator iter = FDRFreq.begin();
while (iter != FDRFreq.end()){
if( iter->fData.size() < StandardNum){
FDRFreq.erase(iter);
}
else{
++iter;
}
}
This part is used to erase the FDRFreq vector item, in which the data length is smaller than the standard number, but the debug assertion failed: vector iterators incompatible. I am a green hand in C++ STL, thanks for your kindly help.
Your problem is iterator invalidation after the call to std::erase. The warning is triggered by an iterator debugging extensions in your standard library implementation. erase returns an iterator to the new valid location after the erase element and you continue iterating from there. However, this is still very inefficient.
Use the Erase-Remove Idiom to remove data with a predicate from a vector.
FDRFreq.erase(std::remove_if(
begin(FDRFreq), end(FDRFreq),
[&StandardNum](const AlignedFDRData& x) {
return fData.size() > StandardNum; }),
end(FDRFreq));
Your code needs to become
while (iter != FDRFreq.end()){
if( iter->fData.size() < StandardNum){
iter = FDRFreq.erase(iter);
}
else{
++iter;
}
}
"vector iterators incompatible" means that the iterator you're using has been invalidated - that is to say, there is no guarantee that the elements it points to still exist at that memory location. An erase of a vector element invalidates the iterators following that location. .erase returns a new, valid iterator you can use instead.
If you're new to STL, I highly recommend you read Scott Myer's Effective STL (and Effective C++, while you're at it)
According to some STL documentation I found, inserting or deleting elements in an std::list does not invalidate iterators. This means that it is allowed to loop over a list (from begin() to end()), and then add elements using push_front.
E.g., in the following code, I initialize a list with elements a, b and c, then loop over it and perform a push_front of the elements. The result should be cbaabc, which is exactly what I get:
std::list<std::string> testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");
for (std::list<std::string>::iterator itList = testList.begin(); itList != testList.end(); ++itList)
testList.push_front(*itList);
for (std::list<std::string>::const_iterator itList = testList.begin(); itList != testList.end(); ++itList)
std::cout << *itList << std::endl;
When I use reverse iterators (loop from rbegin() to rend()) and use push_back, I would expect similar behavior, i.e. a result of abccba. However, I get a different result:
std::list<std::string> testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");
for (std::list<std::string>::reverse_iterator itList = testList.rbegin(); itList != testList.rend(); ++itList)
testList.push_back(*itList);
for (std::list<std::string>::const_iterator itList = testList.begin(); itList != testList.end(); ++itList)
std::cout << *itList << std::endl;
The result is not abccba, but abcccba. That's right there is one additional c added.
It looks like the first push_back also changes the value of the iterator that was initialized with rbegin(). After the push_back it does not point anymore to the 3rd element in the list (which was previously the last one), but to the 4th element (which is now the last one).
I tested this with both Visual Studio 2010 and with GCC and both return the same result.
Is this an error? Or some strange behavior of reverse iterators that I'm not aware of?
The standard says that iterators and references remain valid during an insert. It doesn't say anything about reverse iterators. :-)
The reverse_iterator returned by rbegin() internally holds the value of end(). After a push_back() this value will obviously not be the same as it was before. I don't think the standard says what it should be. Obvious alternatives include the previous last element of the list, or that it stays at the end if that is a fixed value (like a sentinel node).
Technical details: The value returned by rend() cannot point before begin(), because that is not valid. So it was decided that rend() should contain the value of begin() and all other reverse iterators be shifted one position further. The operator* compensates for this and accesses the correct element anyway.
First paragraph of 24.5.1 Reverse iterators says:
Class template reverse_iterator is an iterator adaptor that iterates from the end of the sequence defined
by its underlying iterator to the beginning of that sequence. The fundamental relation between a reverse
iterator and its corresponding iterator i is established by the identity:
&*(reverse_iterator(i)) == &*(i - 1).
I think to understand this, it's best to start by re-casting the for loop as a while loop:
typedef std::list<std::string> container;
container testList;
testList.push_back("a");
testList.push_back("b");
testList.push_back("c");
container::reverse_iterator itList = testList.rbegin();
while (itList != testList.rend()) {
testList.push_back(*itList);
++itList;
}
Along with that, we have to understand how a reverse_iterator works in general. Specifically a reverse_iterator really points to the element after the one you get when you dereference it. end() yields an iterator to just after the end of the container -- but for things like arrays, there's no defined way to point to just before the beginning of a container. What C++ does instead is have the iterator start from just after the end, and progress to the beginning, but when you dereference it, you get the element just before where it actually points.
That means your code actually works like this:
After that, you get pretty much what you expect, pushing back B and then A, so you end up with ABCCCBA.
Try using an iterator for both. Try:
std::list<std::string>::iterator i = testList.end();
and reverse through with --i
The code:
for(x=abc.begin();x!=abc.end();x++)
{
if(-----)
{
----
abc.erase(x);
}
}
And the error is :::
Dangerous iterator usage
After erase the iterator is invalid so dereferencing it or comparing it with another iterator is invalid.
what is the wrong usage in using erase function in the above code?
The itarator x is invalid after deleting the corresponding value from abc. This should fix it:
x = abc.begin();
while(x != abc.end())
{
if (-----)
{
----
x = abc.erase(x);
// skipped only to next item
}
else
{ // skip only to next item
++x;
}
}
The erase template functions of STL containers return the next element, or end().
Edit: Thanks for comment by templatetypedef.
You're using x as the control variable in the loop. Since it is invalidated by erase(), you cannot be sure that it is safe (or meaningful) to subsequently increment it at the top of the loop.
x is a pointer into abc. Once you've erased the item pointed to by x, what is x supposed to be pointing to and how is x++ supposed to work?
You said nothing about the container you are iterating on. On the type of the container depends which iterators are invalidated. For sure iterator to erased element is invalid, but for example in std::vector all iterators past erased element will be invalid (including end()). And for unknown reason although set::erase invalidates only iterator to erased element, it does not return the iterator to next element.
So with std::set:
while (x != abc.end()) // end() will not change and even can be stored
{
if (...)
abc.erase(x++); // increments before erasing
else
++x;
}