Say I have a vector
with various entries, which I want to insert into another vector, while leaving out entries that satisfy a condition.
For example, I want to insert a vector while leaving out all three's.
{1, 3, 2, 3, 4, 5, 3} -> { /* previous content, */ 1, 2, 4, 5}
What I came up with so far uses std::partition
, which does not preserve the relative order and rearranges the source vector.
std::vector<int> source({1, 3, 2, 3, 4, 5, 3});
std::vector<int> target;
auto partition = std::partition(std::begin(source),
std::end(source), [](const auto& a) { return a == 3; });
target.insert(std::begin(target), partition, std::end(source));
What I am looking for is more of an iterator that checks a condition and moves on if the condition is not satisfied. Something like this:
target.insert(std::begin(target),
conditional_begin(source, [](const auto& a) { return a != 3; },
conditional_end(source));
I suppose a conditional_end
function would be necessary, since std::end
would return a different iterator type than conditional_begin
.
Maybe I have overlooked something, so my questions are:
- Does the standard library provide something similar?
- Is there a different easy way to achieve my goal?
- Is there an easy way to implement the conditional iterator functionality?
As ranges will be standardized soon, this is an alternative using range-v3, the reference library for the proprosal:
Yes, you can create a custom iterator that does what you want but it is currently a little tedious to create custom iterators using standard C++. It would look something like this:
You can then create something like the
conditional_begin
andconditional_end
helper functions you asked for. The only issue is thatstd::vector::insert
expects the two iterators to have the same type. If we use a lambda for our condition then this will be part of the type of our conditional iterator. So we need to pass the lambda to both helper functions so that they return iterators with matching types:Which you could call with a lambda like this:
Live demo.
My crude tests show, in this case, this ends up being significantly faster than simply using
copy_if
andback_inserter
becausestd::vector::insert
first works out how much memory to allocate before inserting. Just usingback_inserter
will cause multiple memory allocations. The difference in performance will depend on how expensive the condition is to evaluate. You can get the same speedup by usingcount_if
to reserve enough space before usingcopy_if
:Live demo.
Yes, the standard already has this functionality built in. The function you are looking for is
std::copy_if
.Here,
std::back_inserter(target)
, will callpush_back
ontarget
for each element that the predicate returnstrue
.