Solution 1
template<typename Fwd, typename Out, typename Operation>
Fwd move_if(Fwd first, Fwd last, Out result, Operation op)
{
Fwd swap_pos = first;
for( ; first != last; ++first ) {
if( !op(*first) ) *swap_pos++ = *first;
else *result++ = *first;
}
return swap_pos;
}
The idea is simple. What you want to do is remove elements from one container and place them in another if a predicate is true. So take the code of the std::remove()
algorithm, which already does the remove part, and adapt it to your extra needs. In the code above I added the else
line to copy the element when the predicate is true.
Notice that because we use the std::remove()
code, the algorithm doesn't actually shrink the input container. It does return the updated end iterator of the input container though, so you can just use that and disregard the extra elements. Use the erase-remove idiom if you really want to shrink the input container.
Solution 2
template<typename Bidi, typename Out, typename Operation>
Bidi move_if(Bidi first, Bidi last, Out result, Operation op)
{
Bidi new_end = partition(first, last, not1(op));
copy(new_end, last, result);
return new_end;
}
The second approach uses the STL to implement the algorithm. I personally find it more readable than the first solution, but it has two drawbacks: First, it requires the more-powerful bidirectional iterators for the input container, rather than the forward iterators we used in the first solution. Second, and this is may or may not be an issue for you, the containers are not guaranteed to have the same ordering as before the call to std::partition()
. If you wish to maintain the ordering, replace that call with a call to std::stable_partition()
. std::stable_partition()
might be slightly slower, but it has the same runtime complexity as std::partition()
.
Either Way: Calling the Function
list<int>::iterator p = move_if(l1.begin(), l1.end(),
back_inserter(l2),
bind2nd(less<int>(), 3));
Final Remarks
While writing the code I encountered a dilemma: what should the move_if()
algorithm return? On the one hand the algorithm should return an iterator pointing to the new end position of the input container, so the caller can use the erase-remove idiom to shrink the container. But on the other hand the algorithm should return the position of the end of the result container, because otherwise it could be expensive for the caller to find it. In the first solution the result
iterator points to this position when the algorithm ends, while in the second solution it is the iterator returned by std::copy()
that points to this position. I could return a pair of iterators, but for the sake of making things simple I just return one of the iterators.