The standard std::copy_if & std::transform support execution policies (e.g., std::execution::par_unseq), so a standard std::copy_if_and_transform would also do so and allow one to filter & transform in parallel, without having to create an intermediate sequence of elements (copy_if) and then transform that.
None of the "do it yourself" suggestions above seem to be able to do so.
So I too wonder why the standard didn't include a copy_if_and_transform algorithm.
Nikos' answer above (https://stackoverflow.com/a/70523558/20396957) (which I like a lot, as it introduced me to ranges!) uses ranges to do this lazily.
But "lazily" doesn't necessarily guarantee an execution policy - they could all be computed sequentially for all I know.
So, do we still need the copy_if_and_transform?
And is the newer standard (C++23) going to provide it?
(and same question for remove_if_and_transform while I'm at it, since one may want to do the filter/transform in place instead of constructing a new container)
EDIT: Here's code I've written to implement the (policy taking) copy_if_and_transform using the standard copy_if - hope it helps!
I'd love to hear comments about it and how one can improve it (my generic programming skills are not very good).
Solution - What's the idea:
The copy_if uses *first1 twice - once to call pred() on it and the second time to assign it to *d_first.
I want to be able to hijack the 2nd call, so as to call the transform operation.
So I proxy the input iterator so that it returns a proxy_val instead.
Then I wrap the pred so it can take a proxy_val and apply itself to the actual value.
While proxy_val also offers a way to get the output iterator's element type, upon which it calls the transform operation.
#include <iostream>
#include <string>
#include <vector>
#include <functional>
#include <algorithm>
#include <execution>
#include <iterator>
#include <utility>
// Get the container element type from an iterator
template<class It, class Itvaluetype>
struct get_value_type {
using value_type = std::iter_value_t<It>;
};
// Get the container element type from an inserter
template<class It>
struct get_value_type<It, void> {
using value_type = typename It::container_type::value_type ;
};
template< class ExecutionPolicy, class InputIt, class OutputIt,
class UnaryPredicate, class UnaryOperation>
OutputIt copy_if_and_transform(ExecutionPolicy&& policy,
InputIt first1, InputIt last1,
OutputIt d_first,
UnaryPredicate pred,
UnaryOperation unary_op) {
if (first1 != last1) {
using InputElementType
= std::iterator_traits<InputIt>::value_type;
using OutputElementType
= get_value_type< OutputIt, typename std::iterator_traits< OutputIt > ::value_type >::value_type ;
class proxy_val {
UnaryOperation op;
public:
InputElementType val;
proxy_val(const InputElementType &vl
, UnaryOperation o
) : op(o) , val(vl) {}
operator OutputElementType() const {return op(val);}
};
class proxy_InputIt {
InputIt ii;
UnaryOperation op;
public:
proxy_InputIt(const InputIt &an_in_it, UnaryOperation o)
: ii(an_in_it) , op(o) {}
proxy_InputIt &operator++() { ++ii; return *this; }
proxy_InputIt operator++(int) { proxy_InputIt prev=*this; ++ii; return prev; }
proxy_val operator*() {return {*ii, op};}
bool operator==(const proxy_InputIt &o) const {return ii == o.ii;}
};
auto pr = [ &pred ]( const proxy_val &p ) {return pred(p.val);};
d_first =
std::copy_if(policy
, proxy_InputIt(first1, unary_op)
, proxy_InputIt(last1, unary_op)
, d_first
, pr
);
}
return d_first;
}
// Test with iterator & inserter
int main() {
std::vector<int> vi = {1, 2, 3, 4};
std::vector<std::string> squares_of_odds(vi.size());
auto e =
copy_if_and_transform(std::execution::par_unseq,
std::begin(vi), std::end(vi)
, std::begin(squares_of_odds)
, [](auto x) {return x%2;}
, [](auto x) {return '|'+std::to_string(x*x)+'|';});
std::cout << "Squares of odd\n";
for ( auto f = begin(squares_of_odds); f != e; ++f )
std::cout << (*f) << std::endl;
std::vector<int> vib = {1, 2, 3, 4};
std::vector<std::string> squares_of_even;
copy_if_and_transform(std::execution::par_unseq,
std::begin(vib), std::end(vib)
, std::back_inserter(squares_of_even)
, [](auto x) {return 0==(x%2);}
, [](auto x) {return '|' + std::to_string(x*x) + '|';} );
std::cout << "Squares of even\n";
for ( auto n : squares_of_even)
std::cout << n << std::endl;
return 0;
}