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;
}