Is there a way to marry std::ranges::transform and std::ranges::copy_if?

I need to copy projections of some elements from one range to another in case they meet the predicate.

  • The std::ranges::copy_if uses projection only to pass the result to
    the predicate, projection result can’t be copied; only the original
    data.
  • The necessary transformation could be done with the
    std::ranges::transform, but it copies all elements, since there is
    no std::ranges::transform_if.

Is there a way to copy the projections without allocating intermediate containers and making excessive intermediate copies?

Thus:

std::ranges::transform(src,temp,op);
std::ranges::copy_if (temp,dest,pred);

works, but requires temp container and excessive copy.

Of course, this can easily be implemented with std::ranges::for_each, but I am curious if there is more straightforward solution in ranges?

And of course I can write something like:

template <class InputIt, class OutputIt, class Pred, class Proj>
void transform_if(InputIt first, InputIt last, OutputIt dest, Pred pred, Proj projection)
{
   while (first != last) {
      if (pred(*first)) {
         *dest++ = projection(*first);
      }
      ++first;
   }
}

but I don’t want to reinvent the wheel if there is more simple and straightforward solution in ranges.

  • 1

    I don’t think so. Note that boost contains this function, but it’s just as well to use your version if you’re not already pulling in such a heavy dependency.

    – 

  • 3

    just use std::view::filter in place of std::ranges::copy_if. Which version of C++ standard do you use? 20/23?

    – 




  • @MarekR, I use the latest version of C++ (with all experimental features). My case is that src is the array of indexes and projection gets elements by this indexes from another container. I am not sure std::view::filter will do here, since I don’t have an idea how it would convert dispersed array data in one range. If this could be done, I could prepare demo so that you could pinpoint the std::view::filter usage (in case this can’t be easily answered in the original terms of the question).

    – 

  • 1

    Please provide some example so it is clear what is a goal. Is this ok: godbolt.org/z/xrThhj5vo ?

    – 

  • @MarekR, thank you so much! This is exactly what I was looking for! With one reservation, I wanted to copy it in the resulting vector with std::back_inserter like here, but this last step was easy when you have showed the key part.

    – 

The ranges library includes both range algorithms, which are applied to ranges eagerly, and range adaptors, which are applied to views lazily. Adaptors can be composed into pipelines, so that their actions take place as the view is iterated.

auto result = src | std::views::filter(pred) | std::views::transform(op);
std::ranges::copy(result, dst);

result is lazily evaluated thus this won’t create any unnecessary copies.

Leave a Comment