Auto delete object while cycling vector - c++

I have an std::vector of objects and i cycle it calling some methods of the object. One of them will check a particular condition and if needed will delete itsef from the vector. The point is that erasing the element invalidate the iterator and I cannot continue the loop of it. I found boost::shared_ptr and boost::weak_ptr, could these fix the problem deleting the object after calling all methods and after incremented the iterator? If so, how?
EDIT 1
class CPippo
{
public:
void Pippo();
void Pippo2();
}
class CPippoManager
{
public:
void PipppManager();
void RemovePippo(CPippo *pippo);
private:
std::vector<CPippo*> pippoVector;
}
void CPippo::Pippo()
{
...
if (condition)
{
pippoManager->RemovePippo(this);
}
}
void CPippo::Pippo2()
{
...
}
void CPippoManager::RemovePippo(CPippo *pippo)
{
this->pippoVector.erase(this->pippoVector.begin(), this->pippoVector.end(), pippo);
}
void CPippoManager::PipppManager()
{
for (std::vector<CPippo*>::iterator it = this->pippoVector.begin(); it != this->pippoVector.end; ++it)
{
(*it)->Pippo();
(*it)->Pippo2();
}
}

Never mind what your vector contains - the deletion of managed resources can indeed be left to smart pointers, but the more pressing issue is how to manipulate the container itself.
std::vector indeed has very poor iterator invalidation: erasing or inserting invalidates all iterators from the erasee/insertee onwards, so you cannot even use the standard earase(it++) idiom. But neither should you, since erasing from a vector is expensive. The better solution is to use remove/erase and supply a functor that checks the condition for erasure, and then erase everything in one wash:
std::vector<T> v;
v.erase(std::remove_if(v.begin(), v.end(), MyPred), v.end());
Here MyPred is a predicate that implements your criterion. In C++11 this could be a handy lambda.
If your existing algorithm is too involved, perhaps you can adapt the idea of remove in your own algorithm and move the to-be-erased object to the back of the vector with swap, and return the iterator past the last good element at the end of the algorithm. Then you can have your own optional clean-up loop on the range of objects to be deleted, and then call erase on that range.

As you mention calling a member function you should the erase/remove idiom with std::mem_fun_ref
v.erase(std::remove_if(v.begin(), v.end(), std::mem_fun_ref(&Class::function), v.end()));

To safely delete elements from a vector you should use the erase-remove idiom.
EDIT: Lots of good responses posted before mine, so thought I'd add a little extra clarification on remove because it's not immediately obvious how it operates. The remove algorithm doesn't actually remove anything. All it does is shuffle the container such that all the elements to keep are at the beginning of the container. It then returns an iterator to the end of the kept elements (one past the last element to keep) and that's when you use erase to actually shorten the container. For example:
vector v;...
v.erase(remove(v.begin(), v.end(), 99), v.end());
will remove all elements in v with the value 99.

Use the erase-remove idiom:
// You can write a function, a functor or use lambda directly it is up to you!
bool condition(const vector<object>& v);
v.erase( remove_if(v.begin(), v.end(), condition), v.end() );
This is a fantastic article talking about the subject of writing algorithms involving iterators.

You can get a valid iterator back from erase().
for(... it = vec.begin(); it != vec.end(); it++) {
if (condition(it))
it = --vec.erase(it);
}
Of course, you should always use smart pointers to manage resources.

Related

Iterating over std::set<unique_ptr>, how to keep track which ones to remove?

I need to loop over some objects of class T.
They are stored in an std::set<std::unique_ptr<T>> tees.
The main purpose of the loop's body is to use the objects, but by doing that I will also find out when some of the objects are no longer needed and can be deleted.
I am using a range-based for loop for iterating over the unique_ptrs:
for (std::unique_ptr<T> & tee : tees)
I know I cannot call tees.erase(tee) inside the loop (UB). Therefore I should collect the unique_ptrs that need to be deleted in a helper collection. Problem: The pointers are unique, therefore I cannot copy them into the helper collection.
I could collect the raw pointers in a std::set<T*>, but how would I use these after the loop to delete the matching unique_ptrs from the tees collection? Also, collecting the raw pointers again somehow feels wrong when I made the effort to use smart pointers in this problem.
I could switch to shared_ptr, but the pointers would only ever be shared for the purpose of deleting the objects. Does not feel right.
I could switch from range-based for to something else, like handling the iterators myself, and get the next iterator before deleting the entry. But going back to pre-C++11 techniques also does not feel right.
I could switch to std::remove_if. (EDIT: I can't, actually. Explained in comments below this question and below the accepted answer.) The loop's body would move into the unary_predicate lambda. But the main purpose of the loop is not to determine whether the objects should be deleted, but to make use of them, altering them.
The way of least resistance seems to be to go back to iterator-handling, that way I do not even need a helper collection. But I wonder if you can help me with a C++11-ish (or 14,17) solution?
I don't think you are going to find anything easier than
for(auto it = container.begin(), it != container.end();)
{
//use *it here
if(needs_to_be_erased)
it = container.erase(it);
else
++it;
}
since std::set does not provide mutable access to its elements any sort of transform or remove will not work. You would have to build a container of iterators and then after you process the set go through that container of iterators calling erase for each one.
I think you can copy the positions into a new data structure and remove these items in another loop by accessing the new data structure in reverse order.
int counter =0;
vector<int> indices;
for (unique_ptr<T> & tee : tees)
{
if (bCondition)
indices.push_back(counter);
counter++;
}
reverse(indices.begin(), indices.end());
for (int i : indices)
tees.erase(tees.begin() + i);
Not exactly a solution but if you have to do this a lot you could make your own algorithm for it. I guess the reason this is not in the standard library is because the algorithm needs to know the container to perform an erase.
So you could do something like this:
template<typename Cont, typename Pred>
void erase_if(Cont& c, decltype(std::begin(c)) b, decltype(std::end(c)) e, Pred p)
{
while(b != e)
{
if(p(*b))
b = c.erase(b);
else
++b;
}
}
template<typename Cont, typename Pred>
void erase_if(Cont& c, Pred p)
{ erase_if(c, std::begin(c), std::end(c), p); }
Then call it something like:
erase_if(tees, [](std::unique_ptr<int> const& up){
// use up here...
return (*up) & 1; // erase if odd number
});
or
erase_if(tees, std::begin(tees), std::end(tees), [](std::unique_ptr<int> const& up){
// use up here...
return (*up) & 1; // erase if odd number
});

std::remove_if not working properly [duplicate]

This question already has answers here:
Erasing elements from a vector
(6 answers)
Closed 8 years ago.
Here my code. I want remove from vector all elements with successfully called method 'release'.
bool foo::release()
{
return true;
}
// ...
vector<foo> vec;
// ...
remove_if(vec.begin(), vec.end(), [](foo & f) { return f.release() == true; });
// ...
But remove_if not deleting all elements from vector vec. How remove_if works?
std::remove_if re-arranges the elements of the vector such that the elements you want to keep are in the range [vec.begin(), return_iterator) (note the partially open range). So you need to call std::vector::erase to make sure the vector contains only the desired elements. This is called the erase-remove idiom:
auto it = remove_if(vec.begin(),
vec.end(),
[](foo & f) { return f.release() == true; });
vec.erase(it, vec.end());
Here, I have split it into two lines for clarity, but it is often seen as a one-liner.
std::remove and std::remove_if do not actually remove anything but just give you an iterator by which you can then erase elements using the appropriate member function of whatever container you use. In std::vector's case, erase.
I invite you to read this old article from Scott Meyers: "My Most Important C++ Aha! Moments...Ever":
It was thus with considerable shock and a feeling of betrayal that I discovered that applying remove to a container never changes the number of elements in the container, not even if you ask it to remove everything. Fraud! Deceit! False advertising!
Because the remove_if algorithm operates on a range of elements denoted by two forward iterators, it has no knowledge of the underlying container or collection.
Thus, no elements are actually removed from the container. Rather, all elements which don't fit the remove criteria are brought together to the front of the range, in the same relative order.
The remaining elements are left in a valid, but unspecified, state. When this is done, remove returns an iterator pointing one element past the last unremoved element.
To actually eliminate elements from the container, remove should be combined with the container's erase member function (hence the name "erase-remove idiom").
How to use remove-erase idiom for removing empty vectors in a vector?
Erasing elements from a vector
See http://en.wikipedia.org/wiki/Erase-remove_idiom
std::remove_if doesn't actually obliterate erase the elements. What it does is move the elements that satisfies the criteria into the end of the range. It then returns an iterator to the first element of the removed (which are actually just moved) elements. It is on you then to erase that range from the container.
vector<foo> vec;
auto remove_start = remove_if(vec.begin(), vec.end(), [](foo & f) { return f.release() == true; });
vec.erase(remove_start, vec.end());
or
vec.erase(remove_if(vec.begin(), vec.end(),
[](foo & f) { return f.release() == true; }),
vec.end());

std::insert_iterator and iterator invalidation

I tried writing a generic, in place, intersperse function. The function should intersperse a given element into a sequence of elements.
#include <vector>
#include <list>
#include <algorithm>
#include <iostream>
template<typename ForwardIterator, typename InserterFunc>
void intersperse(ForwardIterator begin, ForwardIterator end, InserterFunc ins,
// we cannot use rvalue references here,
// maybe taking by value and letting users feed in std::ref would be smarter
const ForwardIterator::value_type& elem) {
if(begin == end) return;
while(++begin != end) {
// bugfix would be something like:
// begin = (ins(begin) = elem); // insert_iterator is convertible to a normal iterator
// or
// begin = (ins(begin) = elem).iterator(); // get the iterator to the last inserted element
// begin now points to the inserted element and we need to
// increment the iterator once again, which is safe
// ++begin;
ins(begin) = elem;
}
}
int main()
{
typedef std::list<int> container;
// as expected tumbles, falls over and goes up in flames with:
// typedef std::vector<int> container;
typedef container::iterator iterator;
container v{1,2,3,4};
intersperse(v.begin(), v.end(),
[&v](iterator it) { return std::inserter(v, it); },
23);
for(auto x : v)
std::cout << x << std::endl;
return 0;
}
The example works only for containers that do not invalidate their
iterators on insertion. Should I simply get rid of the iterators and
accept a container as the argument or am I missing something about
insert_iterator that makes this kind of usage possible?
The example works only for containers that do not invalidate their iterators on insertion.
Exactly.
Should I simply get rid of the iterators and accept a container as the argument
That would be one possibility. Another would be not making the algorithm in-place (ie. output to a different container/output-iterator).
am I missing something about insert_iterator that makes this kind of usage possible?
No. insert_iterator is meant for repeated inserts to a single place of a container eg. by a transform algorithm.
The problems with your implementation have absolutely nothing to do with the properties of insert_iterator. All kinds of insert iterators in C++ standard library are guaranteed to remain valid, even if you perform insertion into a container that potentially invalidates iterators on insert. This is, of course, true only if all insertions are performed through only through the insert iterator.
In other words, the implementation of insert iterators guarantees that the iterator will automatically "heal" itself, even if the insertion lead to a potentially iterator-invalidating event in the container.
The problem with your code is that begin and end iterators can potentially get invalidated by insertion into certain container types. It is begin and end that you need to worry about in your code, not the insert iterator.
Meanwhile, you do it completely backwards for some reason. You seem to care about refreshing the insert iterator (which is completely unnecessary), while completely ignoring begin and end.

How to delete an element from a vector while looping over it?

I am looping through a vector with a loop such as for(int i = 0; i < vec.size(); i++). Within this loop, I check a condition on the element at that vector index, and if a certain condition is true, I want to delete that element.
How do I delete a vector element while looping over it without crashing?
The idiomatic way to remove all elements from an STL container which satisfy a given predicate is to use the remove-erase idiom. The idea is to move the predicate (that's the function which yields true or false for some element) into a given function, say pred and then:
static bool pred( const std::string &s ) {
// ...
}
std::vector<std::string> v;
v.erase( std::remove_if( v.begin(), v.end(), pred ), v.end() );
If you insist on using indices, you should not increment the index for every element, but only for those which didn't get removed:
std::vector<std::string>::size_type i = 0;
while ( i < v.size() ) {
if ( shouldBeRemoved( v[i] ) ) {
v.erase( v.begin() + i );
} else {
++i;
}
}
However, this is not only more code and less idiomatic (read: C++ programmers actually have to look at the code whereas the 'erase & remove' idiom immediately gives some idea what's going on), but also much less efficient because vectors store their elements in one contiguous block of memory, so erasing on positions other than the vector end also moves all the elements after the segment erased to their new positions.
If you cannot use remove/erase (e.g. because you don't want to use lambdas or write a predicate), use the standard idiom for sequence container element removal:
for (auto it = v.cbegin(); it != v.cend() /* not hoisted */; /* no increment */)
{
if (delete_condition)
{
it = v.erase(it);
}
else
{
++it;
}
}
If possible, though, prefer remove/erase:
#include <algorithm>
v.erase(std::remove_if(v.begin(), v.end(),
[](T const & x) -> bool { /* decide */ }),
v.end());
Use the Erase-Remove Idiom, using remove_if with a predicate to specify your condition.
if(vector_name.empty() == false) {
for(int i = vector_name.size() - 1; i >= 0; i--)
{
if(condition)
vector_name.erase(vector_name.at(i));
}
}
This works for me. And Don't need to think about indexes have already erased.
Iterate over the vector backwards. That way, you don't nuke the ability to get to the elements you haven't visited yet.
I realize you are asking specifically about removing from vector, but just wanted to point out that it is costly to remove items from a std::vector since all items after the removed item must be copied to new location. If you are going to remove items from the container you should use a std::list. The std::list::erase(item) method even returns the iterator pointing to the value after the one just erased, so it's easy to use in a for or while loop. Nice thing too with std::list is that iterators pointing to non-erased items remain valid throughout list existence. See for instance the docs at cplusplus.com.
That said, if you have no choice, a trick that can work is simply to create a new empty vector and add items to it from the first vector, then use std::swap(oldVec, newVec), which is very efficient (no copy, just changes internal pointers).

What's wrong with my vector<T>::erase here?

I have two vector<T> in my program, called active and non_active respectively. This refers to the objects it contains, as to whether they are in use or not.
I have some code that loops the active vector and checks for any objects that might have gone non active. I add these to a temp_list inside the loop.
Then after the loop, I take my temp_list and do non_active.insert of all elements in the temp_list.
After that, I do call erase on my active vector and pass it the temp_list to erase.
For some reason, however, the erase crashes.
This is the code:
non_active.insert(non_active.begin(), temp_list.begin(), temp_list.end());
active.erase(temp_list.begin(), temp_list.end());
I get this assertion:
Expression:("_Pvector == NULL || (((_Myvec*)_Pvector)->_Myfirst <= _Ptr && _Ptr <= ((_Myvect*)_Pvector)->_Mylast)",0)
I've looked online and seen that there is a erase-remove idiom, however not sure how I'd apply that to a removing a range of elements from a vector<T>
I'm not using C++11.
erase expects a range of iterators passed to it that lie within the current vector. You cannot pass iterators obtained from a different vector to erase.
Here is a possible, but inefficient, C++11 solution supported by lambdas:
active.erase(std::remove_if(active.begin(), active.end(), [](const T& x)
{
return std::find(temp_list.begin(), temp_list.end(), x) != temp_list.end();
}), active.end());
And here is the equivalent C++03 solution without the lambda:
template<typename Container>
class element_of
{
Container& container;
element_of(Container& container) : container(container) {}
public:
template<typename T>
bool operator()(const T& x) const
{
return std::find(container.begin(), container.end(), x)
!= container.end();
}
};
// ...
active.erase(std::remove_if(active.begin(), active.end(),
element_of<std::vector<T> >(temp_list)),
active.end());
If you replace temp_list with a std::set and the std::find_if with a find member function call on the set, the performance should be acceptable.
The erase method is intended to accept iterators to the same container object. You're trying to pass in iterators to temp_list to use to erase elements from active which is not allowed for good reasons, as a Sequence's range erase method is intended to specify a range in that Sequence to remove. It's important that the iterators are in that sequence because otherwise we're specifying a range of values to erase rather than a range within the same container which is a much more costly operation.
The type of logic you're trying to perform suggests to me that a set or list might be better suited for the purpose. That is, you're trying to erase various elements from the middle of a container that match a certain condition and transfer them to another container, and you could eliminate the need for temp_list this way.
With list, for example, it could be as easy as this:
for (ActiveList::iterator it = active.begin(); it != active.end();)
{
if (it->no_longer_active())
{
inactive.push_back(*it);
it = active.erase(it);
}
else
++it;
}
However, sometimes vector can outperform these solutions, and maybe you have need for vector for other reasons (like ensuring contiguous memory). In that case, std::remove_if is your best bet.
Example:
bool not_active(const YourObjectType& obj);
active_list.erase(
remove_if(active_list.begin(), active_list.end(), not_active),
active_list.end());
More info on this can be found under the topic, 'erase-remove idiom' and you may need predicate function objects depending on what external states are required to determine if an object is no longer active.
You can actually make the erase/remove idiom usable for your case. You just need to move the value over to the other container before std::remove_if possibly shuffles it around: in the predicate.
template<class OutIt, class Pred>
struct copy_if_predicate{
copy_if_predicate(OutIt dest, Pred p)
: dest(dest), pred(p) {}
template<class T>
bool operator()(T const& v){
if(pred(v)){
*dest++ = v;
return true;
}
return false;
}
OutIt dest;
Pred pred;
};
template<class OutIt, class Pred>
copy_if_predicate<OutIt,Pred> copy_if_pred(OutIt dest, Pred pred){
return copy_if_predicate<OutIt,Pred>(dest,pred);
}
Live example on Ideone. (I directly used bools to make the code shorter, not bothering with output and the likes.)
The function std::vector::erase requires the iterators to be iterators into this vector, but you are passing iterators from temp_list. You cannot erase elements from a container that are in a completely different container.
active.erase(temp_list.begin(), temp_list.end());
You try to erase elements from one list, but you use iterators for second list. First list iterators aren't the same, like in second list.
I would like to suggest that this is an example of where std::list should be used. You can splice members from one list to another. Look at std::list::splice()for this.
Do you need random access? If not then you don't need a std::vector.
Note that with list, when you splice, your iterators, and references to the objects in the list remain valid.
If you don't mind making the implementation "intrusive", your objects can contain their own iterator value, so they know where they are. Then when they change state, they can automate their own "moving" from one list to the other, and you don't need to transverse the whole list for them. (If you want this sweep to happen later, you can get them to "register" themselves for later moving).
I will write an algorithm here now to run through one collection and if a condition exists, it will effect a std::remove_if but at the same time will copy the element into your "inserter".
//fwd iterator must be writable
template< typename FwdIterator, typename InputIterator, typename Pred >
FwdIterator copy_and_remove_if( FwdIterator inp, FwdIterator end, InputIterator outp, Pred pred )
{
for( FwdIterator test = inp; test != end; ++test )
{
if( pred(*test) ) // insert
{
*outp = *test;
++outp;
}
else // keep
{
if( test != inp )
{
*inp = *test;
}
++inp;
}
}
return inp;
}
This is a bit like std::remove_if but will copy the ones being removed into an alternative collection. You would invoke it like this (for a vector) where isInactive is a valid predicate that indicates it should be moved.
active.erase( copy_and_remove_if( active.begin(), active.end(), std::back_inserter(inactive), isInactive ), active.end() );
The iterators you pass to erase() should point into the vector itself; the assertion is telling you that they don't. This version of erase() is for erasing a range out of the vector.
You need to iterate over temp_list yourself and call active.erase() on the result of dereferencing the iterator at each step.