3

I'd like to apply a function UnaryFunction f to some elements of a std container, given a predicate UnaryPredicate p - sort of what you would get if you combine std::partition and then apply std::for_each to one of the partitions.

I'm quite new to C++, so forgive my ignorance. I have, however, looked for a suitable implementation in <algorithm>, yet I can't seem to find the desired function.

Based on the possible implementations over at cppreference.com, I've come up with the following:

template<class InputIt, class UnaryPredicate, class UnaryFunction>
UnaryFunction for_each_if(InputIt first, InputIt last, UnaryPredicate p, UnaryFunction f)
{
    for (; first != last; ++first) {
        if (p(*first))
        {
            f(*first);    
        }        
    }
    return f;
}

The return value is modeled as per std::for_each, although an OutputIter might have been a better choice. This would require a more convoluted implementation though, and so I've chosen brevity over finesse this time around. The alternative implementation is left as an exercise to the reader.

My question is: is there already an established way to do this in the std library? If not, would this be a reasonable implementation of such a function template?

conciliator
  • 6,078
  • 6
  • 41
  • 66
  • Why don't you use std::partition followed by std::for_each (operating on range based on returned iterator from partition)? – user2672165 Jan 02 '15 at 07:55
  • 2
    There isn't such an algorithm in std lib. You can combine `std::for_each` and `boost::filter_iterator`, however. – jrok Jan 02 '15 at 07:57
  • 2
    Doesn't look like `algorithm` has a function like what you are looking for. I would use the name `for_each_if`. – R Sahu Jan 02 '15 at 07:57
  • 1
    user2672165: Good question. Two reasons, actually. First, the container is shared, and may not get re-ordered. Second, because it will be faster than what you suggest. – conciliator Jan 02 '15 at 07:58
  • 2
    Then can't you just encapsulate the predicate into the function? I mean creating a new function containing both f and p. – user2672165 Jan 02 '15 at 08:00
  • Ehm ... yup. That's obviously a good way. (Facepalm). :) – conciliator Jan 02 '15 at 08:02
  • 1
    if OP have no problem with (header only) dependencies, you may be interested in LINQ for C++. There is a link on isocpp.org some weeks ago: [here](http://www.drdobbs.com/cpp/linq-like-list-manipulation-in-c/240166882?pgno=1) and [here](http://cpplinq.codeplex.com/) – CodeSettler Jan 02 '15 at 09:28
  • CodeSettler: I don't have a problem with dependencies, although in my current project I guess I'd rather not. Thanks for bringing LINQ for C++ to my attention, though! :) – conciliator Jan 03 '15 at 07:36

2 Answers2

4

STL does not support composition of algorithms very well. As you said, you could first call partition, and then call for_each on one of the partitions if you don't care about the order of elements.

For a new project, or one where you can introduce libraries, I would strongly recommend to have a look at a range library, e.g. Boost.Range or Eric Niebler's range-v3.

With the range library, it can be done like this:

template<typename R, typename P, typename F> 
F for_each_if(R& rng, P pred, F f)
{ 
    using namespace boost::adaptors;

    return (rng | filtered(pred) | for_each(f));
}
Jens
  • 9,058
  • 2
  • 26
  • 43
  • Any recommendation for Range v3 gets an upvote from me. Note, however, that it requires `constexpr` (and very likely expression SFINAE) so current releases of VC++ can't use it. (BTW, the function should be named `for_each_if`, as `f` may be non-mutating.) – ildjarn Jan 02 '15 at 09:39
  • Thanks Jens! I've never used this part of Boost, and most definitely not range-v3. Thanks for bringing it to my attention! – conciliator Jan 03 '15 at 07:35
1

As far as the comments go, there seems to be no implementation of this in the std library. However, as user2672165 points out, the predicate may be easily included in the function. To illustrate this, see the following modified version of the for_each example over at cppreference.com:

#include <vector>
#include <algorithm>
#include <iostream>

struct Sum {
    Sum() { sum = 0; }
    void operator()(int n) { sum += n; }

    int sum;
};

int main()
{
    std::vector<int> nums{3, 4, 2, 9, 15, 267};

    std::cout << "before: ";
    for (auto n : nums) {
        std::cout << n << " ";
    }
    std::cout << '\n';

    std::for_each(nums.begin(), nums.end(), [](int &n){ if (n > 5) n++; });

    // Calls Sum::operator() for each number
    Sum s = std::for_each(nums.begin(), nums.end(), Sum());

    std::cout << "after:  ";
    for (auto n : nums) {
        std::cout << n << " ";
    }
    std::cout << '\n';
    std::cout << "sum: " << s.sum << '\n';
}

Here, the predicate is added to the function, so that [](int &n){ n++; } now becomes [](int &n){ if (n > 5) n++; } to only apply the function to integer elements greater than 5.

Expected output is

before: 3 4 2 9 15 267 
after:  3 4 2 10 16 268 
sum: 303

Hope this helps someone else out there.

Piotr Skotnicki
  • 46,953
  • 7
  • 118
  • 160
conciliator
  • 6,078
  • 6
  • 41
  • 66