std::ranges::find_last,std::ranges::find_last_if,std::ranges::find_last_if_not(3)

NAME
std::ranges::find_last,std::ranges::find_last_if,std::ranges::find_last_if_not - std::ranges::find_last,std::ranges::find_last_if,std::ranges::find_last_if_not

Synopsis
 Defined in header <algorithm>
 Call signature
 template< std::forward_iterator I, std::sentinel_for<I> S,
           class T,
           class Proj = std::identity > (since
 requires std::indirect_binary_predicate<ranges::equal_to, C++23)
 std::projected<I, Proj>, (until
                        const T*> C++26)
 constexpr ranges::subrange<I>
     find_last( I first, S last, const T& value, Proj proj = {} );
 template< std::forward_iterator I, std::sentinel_for<I> S,
           class Proj = std::identity,
           class T = std::projected_value_t<I, Proj> >
 requires std::indirect_binary_predicate<ranges::equal_to, (since
 std::projected<I, Proj>, C++26)
                        const T*>
 constexpr ranges::subrange<I>
     find_last( I first, S last, const T& value, Proj proj = {} );
 template< ranges::forward_range R,
           class T,
           class Proj = std::identity > (since
 requires std::indirect_binary_predicate<ranges::equal_to, C++23)
                        (until
 std::projected<ranges::iterator_t<R>, Proj>, C++26)
                        const T*>
 constexpr ranges::borrowed_subrange_t<R>
     find_last( R&& r, const T& value, Proj proj = {} );
 template< ranges::forward_range R,
           class Proj = std::identity,
           class T = std::projected_value_t<iterator_t<R>, Proj> >
 requires std::indirect_binary_predicate<ranges::equal_to, (since
                        C++26)
 std::projected<ranges::iterator_t<R>, Proj>, (1)
                        const T*>
 constexpr ranges::borrowed_subrange_t<R>
     find_last( R&& r, const T& value, Proj proj = {} );
 template< std::forward_iterator I, std::sentinel_for<I> S,
           class Proj = std::identity,
           std::indirect_unary_predicate<std::projected<I, Proj>> (3) (since
 Pred > C++23)
 constexpr ranges::subrange<I> (2)
     find_last_if( I first, S last, Pred pred, Proj proj = {} );
 template< ranges::forward_range R, class Proj = std::identity,
 std::indirect_unary_predicate<std::projected<ranges::iterator_t<R>, (since
 Proj>> (4) C++23)
               Pred >
 constexpr ranges::borrowed_subrange_t<R>
     find_last_if( R&& r, Pred pred, Proj proj = {} );
 template< std::forward_iterator I, std::sentinel_for<I> S,
           class Proj = std::identity,
           std::indirect_unary_predicate<std::projected<I, Proj>> (5) (since
 Pred > C++23)
 constexpr ranges::subrange<I>
     find_last_if_not( I first, S last, Pred pred, Proj proj = {} );
 template< ranges::forward_range R, class Proj = std::identity,
 std::indirect_unary_predicate<std::projected<ranges::iterator_t<R>, (since
 Proj>> (6) C++23)
               Pred >
 constexpr ranges::borrowed_subrange_t<R>
     find_last_if_not( R&& r, Pred pred, Proj proj = {} );
   Returns the last element in the range [first, last) that satisfies specific
   criteria:
   1) find_last searches for an element equal to value.
   3) find_last_if searches for the last element in the range [first, last) for which
   predicate pred returns true.
   5) find_last_if_not searches for the last element in the range [first, last) for
   which predicate pred returns false.
   2,4,6) Same as (1,3,5), but uses r as the source range, as if using ranges::begin(r)
   as first and ranges::end(r) as last.
   The function-like entities described on this page are niebloids, that is:
     * Explicit template argument lists cannot be specified when calling any of them.
     * None of them are visible to argument-dependent lookup.
     * When any of them are found by normal unqualified lookup as the name to the left
       of the function-call operator, argument-dependent lookup is inhibited.
   In practice, they may be implemented as function objects, or with special compiler
   extensions.

Parameters
   first, last - the range of elements to examine
   r - the range of the elements to examine
   value - value to compare the elements to
   pred - predicate to apply to the projected elements
   proj - projection to apply to the elements

Return value
   1,2,3) Let i be the last iterator in the range [first, last) for which E is true.
   Returns ranges::subrange<I>{i, last}, or ranges::subrange<I>{last, last} if no such
   iterator is found.
   2,4,6) Same as (1,2,3) but the return type is ranges::borrowed_subrange_t<I>.

Complexity
   At most last - first applications of the predicate and projection.

Notes
   ranges::find_last, ranges::find_last_if, ranges::find_last_if_not have better
   efficiency on common implementations if I models bidirectional_iterator or (better)
   random_access_iterator.
             Feature-test macro Value Std Feature
                        ranges::find_last,
   __cpp_lib_ranges_find_last 202207L (C++23) ranges::find_last_if,
                        ranges::find_last_if_not
   __cpp_lib_algorithm_default_value_type 202403 (C++26) List-initialization for
                        algorithms (1,2)

Possible implementation
   These implementations only show the slower algorithm used when I models
   forward_iterator.
                        find_last (1-2)
 struct find_last_fn
 {
     template<std::forward_iterator I, std::sentinel_for<I> S,
              class Proj = std::identity,
              class T = std::projected_value_t<iterator_t<R>, Proj>>
     requires std::indirect_binary_predicate<ranges::equal_to, std::projected<I, Proj>,
                        const T*>
     constexpr ranges::subrange<I>
         operator()(I first, S last, const T &value, Proj proj = {}) const
     {
         // Note: if I is mere forward_iterator, we may only go from begin to end.
         I found {};
         for (; first != last; ++first)
             if (std::invoke(proj, *first) == value)
                 found = first;
         if (found == I {})
             return {first, first};
         return {found, std::ranges::next(found, last)};
     }
     template<ranges::forward_range R,
              class Proj = std::identity,
              class T = std::projected_value_t<iterator_t<R>, Proj>>
     requires std::indirect_binary_predicate<ranges::equal_to,
                        std::projected<ranges::iterator_t<R>, Proj>,
                        const T*>
     constexpr ranges::borrowed_subrange_t<R>
         operator()(R&& r, const T &value, Proj proj = {}) const
     {
         return this->operator()(ranges::begin(r), ranges::end(r), value, std::ref(proj));
     }
 };
 inline constexpr find_last_fn find_last;
                        find_last_if (3-4)
 struct find_last_if_fn
 {
     template<std::forward_iterator I, std::sentinel_for<I> S,
              class Proj = std::identity,
              std::indirect_unary_predicate<std::projected<I, Proj>> Pred>
     constexpr ranges::subrange<I>
         operator()(I first, S last, Pred pred, Proj proj = {}) const
     {
         // Note: if I is mere forward_iterator, we may only go from begin to end.
         I found {};
         for (; first != last; ++first)
             if (std::invoke(pred, std::invoke(proj, *first)))
                 found = first;
         if (found == I {})
             return {first, first};
         return {found, std::ranges::next(found, last)};
     }
     template<ranges::forward_range R, class Proj = std::identity,
              std::indirect_unary_predicate<std::projected<ranges::iterator_t<R>, Proj>>
                  Pred>
     constexpr ranges::borrowed_subrange_t<R>
         operator()(R&& r, Pred pred, Proj proj = {}) const
     {
         return this->operator()(ranges::begin(r), ranges::end(r),
                        std::ref(pred), std::ref(proj));
     }
 };
 inline constexpr find_last_if_fn find_last_if;
                        find_last_if_not (5-6)
 struct find_last_if_not_fn
 {
     template<std::forward_iterator I, std::sentinel_for<I> S,
              class Proj = std::identity,
              std::indirect_unary_predicate<std::projected<I, Proj>> Pred>
     constexpr ranges::subrange<I>
         operator()(I first, S last, Pred pred, Proj proj = {}) const
     {
         // Note: if I is mere forward_iterator, we may only go from begin to end.
         I found {};
         for (; first != last; ++first)
             if (!std::invoke(pred, std::invoke(proj, *first)))
                 found = first;
         if (found == I {})
             return {first, first};
         return {found, std::ranges::next(found, last)};
     }
     template<ranges::forward_range R, class Proj = std::identity,
              std::indirect_unary_predicate<std::projected<ranges::iterator_t<R>, Proj>>
                  Pred>
     constexpr ranges::borrowed_subrange_t<R>
         operator()(R&& r, Pred pred, Proj proj = {}) const
     {
         return this->operator()(ranges::begin(r), ranges::end(r),
                        std::ref(pred), std::ref(proj));
     }
 };
 inline constexpr find_last_if_not_fn find_last_if_not;

Example
// Run this code
 #include <algorithm>
 #include <cassert>
 #include <forward_list>
 #include <iomanip>
 #include <iostream>
 #include <string_view>
 int main()
 {
     namespace ranges = std::ranges;
     constexpr static auto v = {1, 2, 3, 1, 2, 3, 1, 2};
     {
         constexpr auto i1 = ranges::find_last(v.begin(), v.end(), 3);
         constexpr auto i2 = ranges::find_last(v, 3);
         static_assert(ranges::distance(v.begin(), i1.begin()) == 5);
         static_assert(ranges::distance(v.begin(), i2.begin()) == 5);
     }
     {
         constexpr auto i1 = ranges::find_last(v.begin(), v.end(), -3);
         constexpr auto i2 = ranges::find_last(v, -3);
         static_assert(i1.begin() == v.end());
         static_assert(i2.begin() == v.end());
     }
     auto abs = [](int x) { return x < 0 ? -x : x; };
     {
         auto pred = [](int x) { return x == 3; };
         constexpr auto i1 = ranges::find_last_if(v.begin(), v.end(), pred, abs);
         constexpr auto i2 = ranges::find_last_if(v, pred, abs);
         static_assert(ranges::distance(v.begin(), i1.begin()) == 5);
         static_assert(ranges::distance(v.begin(), i2.begin()) == 5);
     }
     {
         auto pred = [](int x) { return x == -3; };
         constexpr auto i1 = ranges::find_last_if(v.begin(), v.end(), pred, abs);
         constexpr auto i2 = ranges::find_last_if(v, pred, abs);
         static_assert(i1.begin() == v.end());
         static_assert(i2.begin() == v.end());
     }
     {
         auto pred = [](int x) { return x == 1 or x == 2; };
         constexpr auto i1 = ranges::find_last_if_not(v.begin(), v.end(), pred, abs);
         constexpr auto i2 = ranges::find_last_if_not(v, pred, abs);
         static_assert(ranges::distance(v.begin(), i1.begin()) == 5);
         static_assert(ranges::distance(v.begin(), i2.begin()) == 5);
     }
     {
         auto pred = [](int x) { return x == 1 or x == 2 or x == 3; };
         constexpr auto i1 = ranges::find_last_if_not(v.begin(), v.end(), pred, abs);
         constexpr auto i2 = ranges::find_last_if_not(v, pred, abs);
         static_assert(i1.begin() == v.end());
         static_assert(i2.begin() == v.end());
     }
     using P = std::pair<std::string_view, int>;
     std::forward_list<P> list
     {
         {"one", 1}, {"two", 2}, {"three", 3},
         {"one", 4}, {"two", 5}, {"three", 6},
     };
     auto cmp_one = [](const std::string_view &s) { return s == "one"; };
     // find latest element that satisfy the comparator, and projecting pair::first
     const auto subrange = ranges::find_last_if(list, cmp_one, &P::first);
     // print the found element and the "tail" after it
     for (P const& e : subrange)
         std::cout << '{' << std::quoted(e.first) << ", " << e.second << "} ";
     std::cout << '\n';
 #if __cpp_lib_algorithm_default_value_type
     const auto i3 = ranges::find_last(list, {"three", 3}); // (2) C++26
 #else
     const auto i3 = ranges::find_last(list, P{"three", 3}); // (2) C++23
 #endif
     assert(i3.begin()->first == "three" && i3.begin()->second == 3);
 }

Output:
 {"one", 4} {"two", 5} {"three", 6}

See also
   ranges::find_end finds the last sequence of elements in a certain range
   (C++20) (niebloid)
   ranges::find
   ranges::find_if
   ranges::find_if_not finds the first element satisfying specific criteria
   (C++20) (niebloid)
   (C++20)
   (C++20)
   ranges::search searches for a range of elements
   (C++20) (niebloid)
   ranges::includes returns true if one sequence is a subsequence of another
   (C++20) (niebloid)
   ranges::binary_search determines if an element exists in a partially-ordered
   (C++20) range
                        (niebloid)
   ranges::contains
   ranges::contains_subrange checks if the range contains the given element or subrange
   (C++23) (niebloid)
   (C++23)