Is it possible to efficiently check whether STL std::deque erase succeded? - c++

We are trying to use the std::deque erase member function. The return value of the std::deque erase(iterator) member function is a A random access iterator pointing to the new location of the element that followed the last element erased by the function call, which is the container end if the operation erased the last element in the sequence.
We were wondering whether it is possible to efficiently check whether STL std::deque erase succeded. Thank you. An excerpt of our code is shown below:
typedef std::multimap<char *,Range>::const_iterator I;
std::pair<I,I> b = mmultimap.equal_range(TmpPrevMapPtr);
for (I i=b.first; i != b.second; ++i){
std::deque<Range>::iterator iter;
std::deque<Range>::iterator it;
iter = std::lower_bound(ranges_type.begin(),ranges_type.end(),i->second);
if (iter != ranges_type.end() && !(i->second < *iter)){
it = ranges_type.erase(iter);
}
}

std::deque::erase always succeeds (unless of course, it gets an invalid iterator, in which case the results are undefined).

Check if the size of dequeue decreases by the number of elements you erased.
With regards to the concern about performance, Time Complexity for dequeue::size is O(1)
#include <iostream>
#include <deque>
using namespace std;
int main ()
{
unsigned int i;
deque<unsigned int> mydeque;
// set some values (from 1 to 10)
for (i=1; i<=10; i++) mydeque.push_back(i);
cout << "\nmydeque contains:"<<mydeque.size();
// erase the 6th element
mydeque.erase (mydeque.begin()+5);
// erase the first 3 elements:
mydeque.erase (mydeque.begin(),mydeque.begin()+3);
//Total Elements erased = 4
cout << "\nNow mydeque contains:"<<mydeque.size();
return 0;
}

If the erase succeeded the deque will be shorter than it was. Alternatively, wrap your code in a function (which you should be doing in any case) and have the function return whether or not an erase happened.

Related

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.

Why adding to vector does not work while using iterator?

I have two code sample, which do exactly same thing. One is in C++03 and C++11.
C++ 11
int main()
{
vector<int> v = {1,2,3};
int count = 0;
for each (auto it in v)
{
cout << it<<endl;
if (count == 0)
{
count++;
v.push_back(4);//adding value to vector
}
}
return 0;
}
C++ 03
int main()
{
vector<int> v = {1,2,3};
int count = 0;
for (vector<int>::iterator it = v.begin(); it != v.end(); it++)
{
cout << *it<<endl;
if (count == 0)
{
count++;
v.push_back(4);//adding value to vector
}
}
return 0;
}
Both the codes are giving following exception.
Now when I see vector::end() implementation,
iterator end() _NOEXCEPT
{
// return iterator for end of mutable sequence
return (iterator(this->_Mylast, this));
}
Here, inline function clearly takes _Mylast to calculate end. So, when I add, it pointer will be incremented to next location, like _Mylast++. Why I am getting this exception?
Thanks.
A vector stores its elements in contiguous memory. If that memory block needs to be reallocated, iterators become invalid.
If you need to modify the vector's size while iterating, iterate by index instead of iterator.
Another option is to use a different container with a different iterator behavior, for example a list will allow you to continue iterating as you insert items.
And finally, (dare I suggest this?) if you know the maximum size your vector will grow to, .reserve() it before iterating over it. This will ensure it doesn't get reallocated during your loop. I am not sure if this behavior is guaranteed by the standard though (maybe someone can chime in); I would definitely not do it, considering iterating by index is perfectly safe.
Your push_back is invalidating the iterator you're using in the for loop, because the vector is reallocating its memory, which invalidates all iterators to elements of the vector.
The idiomatic solution for this is to use an insert_iterator, like the one you get from calling std::back_insterter on the vector. Then you can do:
#include <iostream>
#include <iterator>
#include <vector>
int main()
{
std::vector<int> v;
auto inserter = std::back_inserter(v);
for(int i=0; i<100; ++i)
inserter = i;
for(const auto item : v)
std::cout << item << '\n';
}
And it will ensure its own validity even through reallocation calls of the underlying container.
Live demo here.

Why can STL vector's iterator point to element which does not exist?

I write test code to find out whether an itrerator of vector can be beyond the end() iterator, like this.
#include <vector>
#include <iostream>
using namespace std;
int main()
{
vector<int> v;
v.push_back(100);
cout << v.capacity() << endl;
vector<int>::iterator itr = v.begin();
for (int i = 0; i < 10000; ++i, ++itr)
if (itr == v.end()) cout << "end at " << i << endl;
cout << *itr << endl;
return 0;
}
printed result:
1
end at 1
0
the vector object v has only 1 capacity, but its iterator can access the memory after the end() iterator. Is there any protection for this situation?
Iterators do not protect against accessing elements that aren't there, because they should guarantee minimal overhead. The same holds for std::vector::operator[]. Accessing an element at or beyond the std::vector::end() using one of these methods yields undefined behavior and there is no way to detect it after the fact.
However, the std::vector::at() member function throws an std::out_of_range in those cases. This exception can be caught and handled gracefully. Implementing this functionality for iterators would introduce runtime overhead because of bounds checking. It would also introduce space overhead, because the iterator would now not only need to know about the current element, but also about the vector that it iterates over.
With vector access, the capacity property is less interesting than the size. The formet holds the internal buffer size and the latter holds the stored element count. Always count <= capacity.
With that said, vectors were designed to replace/upgrade C arrays so their design specifies implicitly the following:
Locality: all items are stored in a single sequential buffer.
Speed: in order to increase speed, no boundary checks are performed.
The latter is not true for debug versions that may implement boundary checking and iterator validation.
To iterate a vector us one of the following code snippets:
for (vector<int>::iterator itr = v.begin(); itr != v.end(); ++itr) {
int item = *itr;
...
}
Or
size_t size = v.size():
for (size_t i = 0; i < size; ++i) {
int item = v[i];
...
}
Note: both code snippets assume you do not add or remove items from the vector.
Some versions of the standard library come with checked iterators for debug builds. These will emit diagnostic errors if you attempt to use them incorrectly.
I know that at least VS 2005+ (aka Dinkumware), STLport and libstdc++ can do this.

vector iterators incompatible while erase from vector

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.

How do I pass a "const_reverse_iterator" parameter to "std::vector::erase()"?

std::vector::erase() does not accept reverse iterator.
Is there any way to call this method with a reverse iterator?
My sample code is:
std::vector<int> MyVector;
for (int i=0; i<10; i++)
{
MyVector.push_back(i);
}
// Now suppose that I want to erase the last three elements
int nEraseCount = 0;
for (std::vector<int>::const_reverse_iterator it=MyVector.rbegin();
it<MyVector.rend(); ++it)
{
MyVector.erase(it);
if (++nEraseCount == 3) break;
}
However, this sample code is not working, because it is a reverse iterator and erase() does not take reverse iterator as its argument.
How do I modify this code so that it works?
You can convert from reverse_iterators to iterators using base() although you need to subtract one to get the one that points to the same element thus rbegin() points to end() and rend() points to begin() (because it is not possible to point to one before the beginning in reality).
You have more of a problem because you are using a const_reverse_iterator which cannot be converted to a non-const one and erase requires non-const iterators. The logic is that you are modifying the collection so you use a non-const iterator.
In your case, you have a bigger problem with your loop as you are removing iterators thus invalidating them, then trying to move back to the previous element.
If you need to remove the last 3 elements then you should use an erase method that takes a range rather than remove them one at a time.
This can be done using MyVector.erase(MyVector.rbegin() + 3).base(), MyVector.end() ) in this particular case as long as you know that MyVector.size() >= 3
I would workaround the problem by not using a reverse iterator. I'll probably write something like that:
std::vector<int> MyVector;
for (int i=0; i<10; i++)
{
MyVector.push_back(i);
}
// Now suppose that I want to erase the last three elements
int nEraseCount = 0;
while (nEraseCount < 3 && !MyVector.empty())
{
MyVector.pop_back();
++nEraseCount;
}
Okay you have several options - you are erasing from the end - so you could:
resize()
if (MyVector.size() > 3)
MyVector.resize(MyVector.size() - 3);
else
MyVector.clear(); // presumably you don't want all anyway!
simple difference
if (MyVector.size() > 3)
MyVector.erase(MyVector.end() - 3, MyVector.end());
else
MyVector.clear(); // presumably you don't want all anyway!
The approach you've taken is not very idiomatic
If you just want to remove N elements at the back:
size_t N = 3;
size_t to_remove = std::min(vec.size(), N);
vec.erase(vec.end() - to_remove, vec.end());
You cannot pass const_iterator or const_reverse_iterator to erase(), as it is readonly iterator!
You should use non-const forward iterator version : std::vector<int>::iterator.