STL algorithm to repeat an erase operation on a second container? - c++

Life gave me the following objects:
std::vector<T1> v1;
std::vector<T2> v2;
typename std::vector<T1>::iterator it_first;
typename std::vector<T1>::iterator it_last;
and the following constraints:
v1.size() == v2.size() > 0
v1.begin() <= it_first <= it_last <= v1.end()
Removing from v1 the range pointed by the two iterators is a trivial single line, but how do I remove the same range also from v2?
I can easily solve this for instance by building v2 iterators using a mix of std::distance/advance, but I was wondering if the STL provides some machinery for this. Something like the erase-remove idiom coupled with a transform operation, maybe? It seems beyond my STL-fu...

When you have an iterator, then you can get the index via std::distance to begin().
When you have an index, then you can get the iterator via begin() + index.
Thats bascially all you need to get the same range of incdices in the second vector.
Btw iterators are to abstract away the index. When you need the index, then work with the index. For erase that would be
size_t start = 1;
size_t end = 42;
std::erase( v1.begin() + start, v1.begin() + end );
std::erase( v2.begin() + start, v2.begin() + end );
I can easily solve this for instance by building v2 iterators using a mix of std::distance/advance, but I was wondering if the STL provides some machinery for this. Something like the erase-remove idiom coupled with a transform operation, maybe? It seems beyond my STL-fu...
I saw the edit only after writing the answer. The thing is std::distance / std::advance is the machinery to switch between iterators and indices.
Consider that iterators in general are for things that can be iterated. You do not even necesarily need a container to iterate. For example you could write an iterator type that lets you iterate integers. This iterator type could be used like this:
std::vector<std::string> x(100);
auto begin = int_iterator(0);
auto end = int_iterator(100);
for ( ; begin != end; ++begin) std::cout << x[ *begin ];
This is no C-ism. The example is perhaps not the best, but the int_iterator itself is on par with the general concept of iterators.
Usually iterators have no knowledge about the underlying container (if there is one). There are few exceptions (eg insert_iterator). And in case of erase of course you need to pass iterators that refer to element in the corresponding container.

Related

why doesn't std::remove_copy_if() actually remove?

Could this be the worst named function in the STL? (rhetorical question)
std::remove_copy_if() doesn't actually appear to do any removing. As best I can tell, it behaves more like copy_if_not.
The negation is a bit confusing, but can be worked around with std::not1(), however I might be misunderstanding something as I cannot fathom what this function has to do with removing - am I missing something?
If not, is there an STL algorithm for conditionally removing (moving?) elements from a container & putting them in another container?
Editing to add an example so readers are less confused.
The following program appears to leave the input range (V1) untouched:
#include <vector>
#include <iostream>
#include <algorithm>
#include <iterator>
using std::cout;
using std::endl;
int main (void)
{
std::vector<int> V1, V2;
V1.push_back(-2);
V1.push_back(0);
V1.push_back(-1);
V1.push_back(0);
V1.push_back(1);
V1.push_back(2);
std::copy(V1.begin(), V1.end(), std::ostream_iterator<int>(cout, " "));
cout << endl;
std::remove_copy_if(
V1.begin(),
V1.end(),
std::back_inserter(V2),
std::bind2nd(std::less<int>(), 0));
std::copy(V2.begin(), V2.end(), std::ostream_iterator<int>(cout, " "));
cout << endl;
std::copy(V1.begin(), V1.end(), std::ostream_iterator<int>(cout, " "));
cout << endl;
}
It outputs:
-2 0 -1 0 1 2
0 0 1 2
-2 0 -1 0 1 2
I was expecting so see something like:
-2 0 -1 0 1 2
0 0 1 2
0 0 1 2 ? ? ?
Where ? could be any value. But I was surprised to see that the input range was untouched, & that the return value is not able to be used with (in this case) std::vector::erase(). (The return value is an output iterator.)
Could this be the worst named function in the STL?
A bit of background information: in the standard library (or the original STL), there are three concepts, the containers, the iterators into those containers and algorithms that are applied to the iterators. Iterators serve as a cursor and accessor into the elements of a range but do not have a reference to the container (as mentioned before, there might not even be an underlying container).
This separation has the nice feature that you can apply algorithms to ranges of elements that do not belong to a container (consider iterator adaptors like std::istream_iterator or std::ostream_iterator) or that, belonging to a container do not consider all elements (std::sort( v.begin(), v.begin()+v.size()/2 ) to short the first half of the container).
The negative side is that, because the algorithm (and the iterator) don't really know of the container, they cannot really modify it, they can only modify the stored elements (which is what they can access). Mutating algorithms, like std::remove or std::remove_if work on this premise: they overwrite elements that don't match the condition effectively removing them from the container, but they do not modify the container, only the contained values, that is up to the caller in a second step of the erase-remove idiom:
v.erase( std::remove_if( v.begin(), v.end(), pred ),
v.end() );
Further more, for mutating algorithms (those that perform changes), like std::remove there is a non-mutating version named by adding copy to the name: std::remove_copy_if. None of the XXXcopyYYY algorithms are considered to change the input sequence (although they can if you use aliasing iterators).
While this is really no excuse for the naming of std::remove_copy_if, I hope that it helps understanding what an algorithm does given its name: remove_if will modify contents of the range and yield a range for which all elements that match the predicate have been removed (the returned range is that formed by the first argument to the algorithm to the returned iterator). std::remove_copy_if does the same, but rather than modifying the underlying sequence, it creates a copy of the sequence in which those elements matching the predicate have been removed. That is, all *copy* algorithms are equivalent to copy and then apply the original algorithm (note that the equivalence is logical, std::remove_copy_if only requires an OutputIterator, which means that it could not possibly copy and then walk the copied range applying std::remove_if.
The same line of reasoning can be applied to other mutating algorithms: reverse reverses the values (remember, iterators don't access the container) in the range, reverse_copy copies the elements in the range to separate range in the reverse order.
If not, is there an STL algorithm for conditionally removing (moving?) elements from a container & putting them in another container?
There is no such algorithm in the STL, but it could be easily implementable:
template <typename FIterator, typename OIterator, typename Pred>
FIterator splice_if( FIterator first, FIterator last, OIterator out, Pred p )
{
FIterator result = first;
for ( ; first != last; ++first ) {
if ( p( *first ) ) {
*result++ = *first;
} else {
*out++ = *first;
}
}
return result;
}
is there an STL algorithm for conditionally removing (moving?) elements from a container & putting them in another container?
The closest thing I can think of is std::stable_partition:
std::vector<int> v;
// ...
auto it = std::stable_partition(v.begin(), v.end(), pick_the_good_elements);
std::vector<int> w(std::make_move_iter(it), std::make_move_iter(v.end()));
v.erase(it, v.end());
Now v will contain the "good" elements, and w will contain the "bad" elements.
If not, is there an STL algorithm for conditionally removing (moving?) elements from a container & putting them in another container?
Not really. The idea is that the modifying algorithms are allowed to "move" (not in the C++ sense of the word) elements in a container around but cannot change the length of the container. So the remove algorithms could be called prepare_for_removal.
By the way, C++11 provides std::copy_if, which allows you to copy selected elements from one container to another without playing funny logic games with remove_copy_if.
You are right, that is what it does... std::remove_copy_if copies the vector, removing anything that matches the pred.
std::remove_if ... removes on condition (or rather, shuffles things around).
I agree that remove is not the best name for this family of functions.
But as Luc said, there's a reason for it working the way it does, and the GoTW item that he mentions explains how it works. remove_if works exactly the same way as remove - which is what you would expect.
You might also want to read this Wikibooks article.

How can I check if this is last member of std::list?

I have a list like below
typedef std::list<std::string> SegmentValue;
then in a iteration I need check if this is last iteration.
for(Field::SegmentValue::const_iterator it = m_segmentValue.begin();It !=
m_segmentValue.end();It++){
if((segIt + 1) == m_segmentValue.end())//last iteration
...
}
but I get error in compile that:
error C2678: binary '+' : no operator found which takes a left-hand operand of type 'std::list<_Ty>::_Const_iterator<_Secure_validation>'
how I can check if this is last itration?
You can't use binary + and - operators with std::list iterators. std::list iterators are bidirectional iterators, but they are not random access iterators, meaning that you can't shift them by an arbitrary constant value.
Use unary ++ and -- instead
Field::SegmentValue::const_iterator it_last = m_segmentValue.end();
--it_last;
Now it_last is the last element iterator. Just make sure it remains valid. If you are not making any iterator-invalidating modifications to your container, you can pre-compute it_last and use it in the cycle. Otherwise, you'll have to re-compute it as necessary.
In fact, in generic algorithms it is always a good idea to prefer using -- and ++ with iterators whenever possible (instead of binary + 1 and - 1), since it reduces your algorithm's requirements: binary + and - require random access iterators, while ++ and -- work with bidirectional ones.
Use std::next:
if (std::next(segIt) == m_segmentValue.end()) ...
If you're using C++03, you can easily write next yourself:
template<typename T> T next(T it, typename std::iterator_traits<T>::difference_type n = 1) {
std::advance(it, n);
return it;
}
Something like this perhaps:
Field::SegmentValue::const_iterator last = m_segmentValue.end()
--last;
for(Field::SegmentValue::const_iterator it = m_segmentValue.begin();
It != m_segmentValue.end();
It++) {
if(It == last) {
// last iteration
}
}
You can only do arithmetic with Random Access Iterators. std::list's iterators are Bidirectional.
See here for what you can and cannot do with iterators of various categories.
Try this:
Field::SegmentValue::const_iterator next = it; ++next;
// or in C++11:
// Field::SegmentValue::const_iterator next = std::next( it );
if( next == m_segmentValue.end()) //last iteration
List iterators are Bidirectional, not RandomAccess so they don't support operator+.
std::list iterator are not random access, they are bidirectional. The operator+ is not supported. You need to use std::vector to do something like that.
How about:
if ( &*it == &*(m_segmentValue.rbegin()))
i.e, comparing the addresses of the segments.

C++ equivalent of Python difference_update?

s1 and s2 are sets (Python set or C++ std::set)
To add the elements of s2 to s1 (set union), you can do
Python: s1.update(s2)
C++: s1.insert(s2.begin(), s2.end());
To remove the elements of s2 from s1 (set difference), you can do
Python: s1.difference_update(s2)
What is the C++ equivalent of this? The code
s1.erase(s2.begin(), s2.end());
does not work, for s1.erase() requires iterators from s1.The code
std::set<T> s3;
std::set_difference(s1.begin(), s1.end(), s2.begin(), s2.end(), std::inserter(s3, s3.end());
s1.swap(s3);
works, but seems overly complex, at least compared with Python.
Is there a simpler way?
Using std::set_difference is the idiomatic way to do this in C++. You have stumbled across one of the primary differences (pun intended) between C++/STL and many other languages. STL does not bundle operations directly with the data structures. This is why std::set does not implement a difference routine.
Basically, algorithms such as std::set_difference write the result of the operation to another object. It is interesting to note that the algorithm does not require that either or both of the operands are actually std::set. The definition of the algorithm is:
Effects: Copies the elements of the range [first1, last1) which are not present in the range [first2, last2) to the range beginning at result. The elements in the constructed range are sorted.
Requires: The resulting range shall not overlap with either of the original ranges. Input ranges are required to be order by the same operator<.
Returns: The end of the constructed range.
Complexity: At most 2 * ((last1 - first1) + (last2 - first2)) - 1 comparisons
The interesting difference is that the C++ version is applicable to any two sorted ranges. In most languages, you are forced to coerce or translate the calling object (left-hand operand) into a set before you have access to the set difference algorithm.
This is not really pertinent to your question, but this is the reason that the various set algorithms are modeled as free-standing algorithms instead of member methods.
You should iterate through the second set:
for( set< T >::iterator iter = s2.begin(); iter != s2.end(); ++iter )
{
s1.erase( *iter );
}
This will could be cheaper than using std::set_difference - set_difference copies the unique objects into a new container, but it takes linear time, while .erase will not copy anything, but is O(n * log( n ) ).
In other words, depends on the container, you could choose the way, that will be faster for your case.
Thanks David Rodríguez - dribeas for the remark! (:
EDIT: Doh! I thought about BOOST_FOREACH at the very beginning, but I was wrong that it could not be used.. - you don't need the iterator, but just the value.. As user763305 said by himself/herself.
In c++ there is no difference method in the set. The set_difference looks much more awkward as it is more generic than applying a difference on two sets. Of course you can implement your own version of in place difference on sets:
template <typename T, typename Compare, typename Allocator>
void my_set_difference( std::set<T,Compare,Allocator>& lhs, std::set<T,Compare,Allocator> const & rhs )
{
typedef std::set<T,Comapre,Allocator> set_t;
typedef typename set_t::iterator iterator;
typedef typename set_t::const_iterator const_iterator;
const_iterator rit = rhs.begin(), rend = rhs.end();
iterator it = lhs.begin(), end = lhs.end();
while ( it != end && rit != rend )
{
if ( lhs.key_comp( *it, *rit ) ) {
++it;
} else if ( lhs.key_comp( *rit, *it ) ) {
++rit;
} else {
++rit;
lhs.erase( it++ );
}
}
}
The performance of this algorithm will be linear in the size of the arguments, and require no extra copies as it modifies the first argument in place.
You can also do it with remove_if writing your own functor for testing existence in a set, e.g.
std::remove_if(s1.begin(), s1.end(), ExistIn(s2));
I suppose that set_difference is more efficient though as it probably scans both sets only once
Python set is unordered, and is more of an equivalent of C++ std::unordered_set than std::set, which is ordered.
David Rodríguez's algorithm relies on the fact that std::set is ordered, so the lhs and rhs sets can be traversed in the way as exhibit in the algorithm.
For a more general solution that works for both ordered and unordered sets, Kiril Kirov's algorithm should be the safe one to adopt if you are enforcing/preserving the "unorderedness" nature of Python set.

Peeking the next element in STL container

Is it possible to peek next element in a container which the iterator currently points to without changing the iterator?
For example in std::set,
int myArray[]= {1,2,3,4};
set <int> mySet(myArray, myArray+4);
set <int>::iterator iter = mySet.begin();
//peek the next element in set without changing iterator.
mySet.erase(iter); //erase the element if next element is n+1
C++0x adds a handy utility function, std::next, that copies an iterator, advances it, and returns the advanced iterator. You can easily write your own std::next implementation:
#include <iterator>
template <typename ForwardIt>
ForwardIt next(ForwardIt it,
typename std::iterator_traits<ForwardIt>::difference_type n = 1)
{
std::advance(it, n);
return it;
}
You can use this in your example like so:
if (iter != mySet.end() && next(iter) != mySet.end() && *next(iter) == *iter + 1)
mySet.erase(iter);
Not with iterators in general. An iterator isn't guaranteed to be able to operate non-destructively. The classic example is an Input Iterator that actually represents an underlying input stream.
There's something that works for this kind of iterator, though. A Forward Iterator doesn't invalidate previous copies of itself by the act of moving forward through the collection. Most iterators (including those for STL collections) are at least Forward Iterators, if not a more functional version- only Input Iterators or Output Iterators are more restricted. So you can simply make a copy of your iterator, increment the copy and check that, then go back to your original iterator.
So your peek code:
set <int>::iterator dupe = iter;
++dupe;
// (do stuff with dupe)
set <int>::iterator iter2 = iter;
++iter2;
int peekedValue = *iter2;
You can always make a copy of the iterator and advance the copy:
set <int>::iterator iter = mySet.begin();
set <int>::iterator iterCopy = iter;
iterCopy++;
if (*iterCopy == something)
mySet.erase(iter);
But beware that iterCopy may no longer be valid once you erase iter.
for sequence containers (vector, deque, and list) you can call front which will give you a peek (more info on the lower part of this link).
This will not work for std::set as its nature does not allow for the [] operator, but for containers that do, you can do:
std::vector<int> v;
v.push_back(3);
v.push_back(4);
std::vector<int>::iterator it = v.begin();
std::cout << v[it - v.begin() + 1];
But this could be dangerous if it points to the last element in the container; but the same applies to the solution above. E.g. you'll have to make checks in both cases.

C++ STL Vectors: Get iterator from index?

So, I wrote a bunch of code that accesses elements in an stl vector by index[], but now I need to copy just a chunk of the vector. It looks like vector.insert(pos, first, last) is the function I want... except I only have first and last as ints. Is there any nice way I can get an iterator to these values?
Try this:
vector<Type>::iterator nth = v.begin() + index;
way mentioned by #dirkgently ( v.begin() + index ) nice and fast for vectors
but std::advance( v.begin(), index ) most generic way and for random access iterators works constant time too.
EDIT
differences in usage:
std::vector<>::iterator it = ( v.begin() + index );
or
std::vector<>::iterator it = v.begin();
std::advance( it, index );
added after #litb notes.
Also; auto it = std::next(v.begin(), index);
Update: Needs a C++11x compliant compiler
You can always use std::advance to move the iterator a certain amount of positions in constant time:
std::vector<int>::iterator it = myvector.begin();
std::advance(it, 2);
Actutally std::vector are meant to be used as C tab when needed. (C++ standard requests that for vector implementation , as far as I know - replacement for array in Wikipedia)
For instance it is perfectly legal to do this folowing, according to me:
int main()
{
void foo(const char *);
sdt::vector<char> vec;
vec.push_back('h');
vec.push_back('e');
vec.push_back('l');
vec.push_back('l');
vec.push_back('o');
vec.push_back('/0');
foo(&vec[0]);
}
Of course, either foo must not copy the address passed as a parameter and store it somewhere, or you should ensure in your program to never push any new item in vec, or requesting to change its capacity. Or risk segmentation fault...
Therefore in your exemple it leads to
vector.insert(pos, &vec[first_index], &vec[last_index]);