c++stdstd-rangesc++23

Why does std::ranges::find_if return an iterator but std::ranges::find_last_if return a subrange?


Reading through cppreference.com, I noticed ranges::find_last_if returns a subrange but ranges::find_if returns only an iterator. I am wondering if there is a good reason for this decision?

Apparently, the correct usage is to use ranges::find_if with a reversed range:

const auto it = std::ranges::find_if(data | std::views::reverse, func);

That said, ranges::find_last_if returning an iterator seems more intuitive to me so I am curious as to its purpose.


Solution

  • Basically, the std::ranges algorithms return the end iterator whenever they can.

    For example,

    The reason is that, unlike the traditional std algorithms, the ranges ones take an iterator and a sentinel. The only thing you can do with a sentinel (that is not itself an iterator) is to compare it with an iterator. If they compare equal, it means the end of range is reached.

    Since a sentinel is less useful that an iterator, and these algorithms need to get an end iterator anyway, they return that iterator to not lose valuable information.

    For std::ranges::find_last_if, this means it should return an iterator to the element it finds, and an end iterator. They naturally form a subrange, and that's what std::ranges::find_last_if actually returns.

    If you don't need the end iterator, you can extract the first iterator using subrange::begin.

    const auto it = std::ranges::find_last_if(data, func).begin();