std::insert_iterator and iterator invalidation - c++

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.

Related

Iterate over std::multimap and delete certain entries

I want to iterate over all items in a std::multimap (all values of all keys), and delete all entries that satisfy some condition:
#include <map>
typedef int KEY_TYPE;
typedef int VAL_TYPE;
bool shouldRemove(const KEY_TYPE&, const VAL_TYPE&);
void removeFromMap(std::multimap<KEY_TYPE,VAL_TYPE>& map){
for (auto it = map.begin(); it != map.end(); it++){
if (shouldRemove(it->first,it->second))
map.erase(it);
}
}
The iteration works unless the first item gets deleted, and the following error is thrown then:
map/set iterator not incrementable
How can the removeFromMap function be rewritten in order to work properly? The code should work for all kinds of key- and value types of the map.
I am using C++ 11 and Visual Studio 2013.
You need to increment your iterator before you do the erase. When you do map.erase(it); the iterator it becomes invalid. However, other iterators in the map will still be valid. Therefore you can fix this by doing a post-increment on the iterator...
auto it = map.begin();
const auto end = map.end();
while (it != end)
{
if (shouldRemove(it->first,it->second))
{
map.erase(it++);
// ^^ Note the increment here.
}
else
{
++it;
}
}
The post-increment applied to it inside the map.erase() parameters will ensure that it remains valid after the item is erased by incrementing the iterator to point to the next item in the map just before erasing.
map.erase(it++);
... is functionally equivalent to...
auto toEraseIterator = it; // Remember the iterator to the item we want to erase.
++it; // Move to the next item in the map.
map.erase(toEraseIterator); // Erase the item.
As #imbtfab points out in the comments, you can also use it = map.erase(it) to do the same thing in C++11 without the need for post-incrementing.
Note also that the for loop has now been changed to a while loop since we're controlling the iterator manually.
Additionally, if you're looking to make your removeFromMap function as generic as possible, you should consider using template parameters and pass your iterators in directly, rather than passing in a reference to the multi-map. This will allow you to use any map-style container type, rather than forcing a multimap to be pased in.
e.g.
template <typename Iterator>
void removeFromMap(Iterator it, const Iterator &end){
...
}
This is how the standard C++ <algorithm> functions do it also (e.g. std::sort(...)).

Why boost filter_iterator has weird make_filter_iterator function?

after some pain I managed to hack together this minimal example of boost filter_iterator
using namespace std;
std::function<bool(uint32_t)> stlfunc= [](uint32_t n){return n%3==0;};
int main()
{
vector<uint32_t> numbers{11,22,33,44,55,66,77,3,6,9};
auto start = boost::make_filter_iterator(stlfunc, numbers.begin(), numbers.end());
auto end = boost::make_filter_iterator(stlfunc, numbers.end() , numbers.end());
auto elem = std::max_element(start,end);
cout << *elem;
}
It works nice, but I wonder why the make_filter_iterator takes numbers.end()?
I might be wrong to use it that way, I guestimated it from the C array example:
http://www.boost.org/doc/libs/1_53_0/libs/iterator/example/filter_iterator_example.cpp
That is explained in the docs:
When skipping over elements, it is necessary for the filter adaptor to
know when to stop so as to avoid going past the end of the underlying
range. A filter iterator is therefore constructed with pair of
iterators indicating the range of elements in the unfiltered sequence
to be traversed.
From the source below you can see the always check if they have reached the end in satisfy_predicate:
void increment()
{
++(this->base_reference());
satisfy_predicate();
}
void satisfy_predicate()
{
while (this->base() != this->m_end && !this->m_predicate(*this->base()))
++(this->base_reference());
}
Also, as pointed out by Alex Chamberlain, the
constructors make it optional when passing the end iterator, for example: filter_iterator(Iterator x, Iterator end = Iterator()); (provided it is default constructible). So, you could omit numbers.end() from your code when constructing the end iterator.
If you look at the declaration of your make_filter_iterator template, you see it looks like this:
template <class Predicate, class Iterator>
filter_iterator<Predicate,Iterator>
make_filter_iterator(Predicate f, Iterator x, Iterator end = Iterator());
Specifically you see that the last parameter is a default parameter, and it's set to Iterator() which would mean it's default contructed and for some types of iterator it behaves like actual end() iterator, which points one past the end of any array, ie it points to garbage.
Most container types do require an actual end() iterator to be passed.

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.

Auto delete object while cycling vector

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.

Now to remove elements that match a predicate?

I have a source container of strings I want to remove any strings from the source container that match a predicate and add them into the destination container.
remove_copy_if and other algorithms can only reorder the elements in the container, and therefore have to be followed up by the erase member function. My book (Josuttis) says that remove_copy_if returns an iterator after the last position in the destination container. Therefore if I only have an iterator into the destination container, how can I call erase on the source container? I have tried using the size of the destination to determine how far back from the end of the source container to erase from, but had no luck. I have only come up with the following code, but it makes two calls (remove_if and remove_copy_if).
Can someone let me know the correct way to do this? I'm sure that two linear calls is not
the way to do this.
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
class CPred : public unary_function<string, bool>
{
public:
CPred(const string& arString)
:mString(arString)
{
}
bool operator()(const string& arString) const
{
return (arString.find(mString) == std::string::npos);
}
private:
string mString;
};
int main()
{
vector<string> Strings;
vector<string> Container;
Strings.push_back("123");
Strings.push_back("145");
Strings.push_back("ABC");
Strings.push_back("167");
Strings.push_back("DEF");
cout << "Original list" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
CPred Pred("1");
remove_copy_if(Strings.begin(), Strings.end(),
back_inserter(Container),
Pred);
Strings.erase(remove_if(Strings.begin(), Strings.end(),
not1(Pred)), Strings.end());
cout << "Elements beginning with 1 removed" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
cout << "Elements beginning with 1" << endl;
copy(Container.begin(), Container.end(),ostream_iterator<string>(cout,"\n"));
return 0;
}
With all due respect to Fred's hard work, let me add this: the move_if is no different than remove_copy_if at an abstract level. The only implementation level change is the end() iterator. You are still not getting any erase(). The accepted answer does not erase() the matched elements -- part of the OP's problem statement.
As for the OP's question: what you want is an in-place splice. This is possible for lists. However, with vectors this will not work. Read about when and how and why iterators are invalidated. You will have to take a two pass algorithm.
remove_copy_if and other algorithms can only reorder the elements in the container,
From SGI's documentation on remove_copy_if:
This operation is stable, meaning that the relative order of the elements that are copied is the same as in the range [first, last).
So no relative reordering takes place. Moreover, this is a copy, which means the elements from Source vector in your case, is being copied to the Container vector.
how can I call erase on the source container?
You need to use a different algorithm, called remove_if:
remove_if removes from the range [first, last) every element x such that pred(x) is true. That is, remove_if returns an iterator new_last such that the range [first, new_last) contains no elements for which pred is true. The iterators in the range [new_last, last) are all still dereferenceable, but the elements that they point to are unspecified. Remove_if is stable, meaning that the relative order of elements that are not removed is unchanged.
So, just change that remove_copy_if call to:
vector<string>::iterator new_last = remove_if(Strings.begin(),
Strings.end(),
Pred);
and you're all set. Just keep in mind, your Strings vector's range is no longer that defined by the iterators [first(), end()) but rather by [first(), new_last).
You can, if you want to, remove the remaining [new_last, end()) by the following:
Strings.erase(new_last, Strings.end());
Now, your vector has been shortened and your end() and new_last are the same (one past the last element), so you can use as always:
copy(Strings.begin(), Strings.end(), ostream_iterator(cout, "\"));
to get a print of the strings on your console (stdout).
I see your point, that you'd like to avoid doing two passes over your source container. Unfortunately, I don't believe there's a standard algorithm that will do this. It would be possible to create your own algorithm that would copy elements to a new container and remove from the source container (in the same sense as remove_if; you'd have to do an erase afterward) in one pass. Your container size and performance requirements would dictate whether the effort of creating such an algorithm would be better than making two passes.
Edit: I came up with a quick implementation:
template<typename F_ITER, typename O_ITER, typename FTOR>
F_ITER move_if(F_ITER begin, F_ITER end, O_ITER dest, FTOR match)
{
F_ITER result = begin;
for(; begin != end; ++begin)
{
if (match(*begin))
{
*dest++ = *begin;
}
else
{
*result++ = *begin;
}
}
return result;
}
Edit:
Maybe there is confusion in what is meant by a "pass". In the OP's solution, there is a call to remove_copy_if() and a call to remove_if(). Each of these will traverse the entirety of the original container. Then there is a call to erase(). This will traverse any elements that were removed from the original container.
If my algorithm is used to copy the removed elements to a new container (using begin() the original container for the output iterator will not work, as dirkgently demonstrated), it will perform one pass, copying the removed elements to the new container by means of a back_inserter or some such mechanism. An erase will still be required, just as with remove_if(). One pass over the original container is eliminated, which I believe is what the OP was after.
There will be copy_if and remove_if.
copy_if( Strings.begin(), Strings.end(),
back_inserter(Container), not1(Pred) );
Strings.erase( remove_if( Strings.begin(), Strings.end(), not1(Pred) ),
Strings.end() );
It is better to understand code where Predicate class answering "true" if something is present. In that case you won't need not1 two times.
Because std::find looks for substring not obligatory from the begin you need to change "beginning with 1" to "with 1" to avoid future misunderstanding of your code.
The whole reason why the remove_* algorithms do not erase elements is because it is impossible to "erase" an element by the iterator alone. You can't get container by iterator
This point is explained in more details in the book "Effective STL"
Use 'copy_if', followed by 'remove_if'. remove_copy_if does not modify the source.
On lists you can do better - reordering followed by splice.
If you don't mind having your strings in the same container, and having just an iterator to separate them, this code works.
#include "stdafx.h"
#include <iostream>
#include <iterator>
#include <vector>
#include <string>
#include <algorithm>
#include <functional>
using namespace std;
class CPred : public unary_function<string, bool>
{
public:
CPred(const string& arString)
:mString(arString)
{
}
bool operator()(const string& arString) const
{
return (arString.find(mString) == std::string::npos);
}
private:
string mString;
};
int main()
{
vector<string> Strings;
Strings.push_back("213");
Strings.push_back("145");
Strings.push_back("ABC");
Strings.push_back("167");
Strings.push_back("DEF");
cout << "Original list" << endl;
copy(Strings.begin(), Strings.end(),ostream_iterator<string>(cout,"\n"));
CPred Pred("1");
vector<string>::iterator end1 =
partition(Strings.begin(), Strings.end(), Pred);
cout << "Elements matching with 1" << endl;
copy(end1, Strings.end(), ostream_iterator<string>(cout,"\n"));
cout << "Elements not matching with 1" << endl;
copy(Strings.begin(), end1, ostream_iterator<string>(cout,"\n"));
return 0;
}
remove*() don't relally remove elements, it simply reorders them and put them at the end of the collection and return a new_end iterator in the same container indicating where the new end is. You then need to call erase to remove the range from the vector.
source.erase(source.remove(source.begin(), source.end(), element), source.end());
remove_if() does the same but with a predicate.
source.erase(source.remove_if(source.begin(), source.end(), predicate), source.end());
remove_copy_if() will only copy the elements NOT matching the predicate, leaving the source vector intact and providing you with the end iterator on the target vector, so that you can shrink it.
// target must be of a size ready to accomodate the copy
target.erase(source.remove_copy_if(source.begin(), source.end(), target.begin(), predicate), target.end());