Reading through cppreference.com, I noticed ranges::find_last_if does not return an iterator but ranges::find_if does. 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.
Basically, the std::ranges
algorithms return the end iterator whenever they can.
For example,
std::ranges::for_each
returns a in_fun_result
. The in
component is an iterator to the end of the source range.std::ranges::copy
returns a in_out_result
. in
is an iterator to the end of the source range, and out
is an iterator to the end of the destination range. (std::copy
only returns the end of the destination range.)std::ranges::fill
and std::ranges::generate
return an iterator to the end of the destination range. (The std
algorithms return nothing.)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();