diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst index b548322b1b2b8a..262da3f8937d21 100644 --- a/libcxx/docs/FeatureTestMacroTable.rst +++ b/libcxx/docs/FeatureTestMacroTable.rst @@ -360,6 +360,8 @@ Status ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_contains`` ``202207L`` ---------------------------------------------------------- ----------------- + ``__cpp_lib_ranges_find_last`` ``202207L`` + ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_iota`` *unimplemented* ---------------------------------------------------------- ----------------- ``__cpp_lib_ranges_join_with`` *unimplemented* diff --git a/libcxx/docs/ReleaseNotes/19.rst b/libcxx/docs/ReleaseNotes/19.rst index 784934eafe9f72..43767552960e4f 100644 --- a/libcxx/docs/ReleaseNotes/19.rst +++ b/libcxx/docs/ReleaseNotes/19.rst @@ -58,6 +58,7 @@ Implemented Papers - P2231R1 - Missing ``constexpr`` in ``std::optional`` and ``std::variant`` - P0019R8 - ``std::atomic_ref`` - P2389R2 - Alias template ``dims`` for the ``extents`` of ``mdspan`` +- P1223R5 - ``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()`` Improvements and New Features ----------------------------- diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv index 6dd6bdad076587..f8970320ce1fa6 100644 --- a/libcxx/docs/Status/Cxx23Papers.csv +++ b/libcxx/docs/Status/Cxx23Papers.csv @@ -55,7 +55,7 @@ "`P0429R9 `__","LWG","A Standard ``flat_map``","July 2022","","" "`P1169R4 `__","LWG","``static operator()``","July 2022","|Complete|","16.0" "`P1222R4 `__","LWG","A Standard ``flat_set``","July 2022","","" -"`P1223R5 `__","LWG","``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()``","July 2022","","","|ranges|" +"`P1223R5 `__","LWG","``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()``","July 2022","|Complete|","19.0","|ranges|" "`P1467R9 `__","LWG","Extended ``floating-point`` types and standard names","July 2022","","" "`P1642R11 `__","LWG","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","July 2022","","" "`P1899R3 `__","LWG","``stride_view``","July 2022","","","|ranges|" diff --git a/libcxx/docs/Status/RangesAlgorithms.csv b/libcxx/docs/Status/RangesAlgorithms.csv index f7a51f732c4b14..469ea21a76aab9 100644 --- a/libcxx/docs/Status/RangesAlgorithms.csv +++ b/libcxx/docs/Status/RangesAlgorithms.csv @@ -1,8 +1,8 @@ Standard,Algorithm,Assignee,CL,Status C++20,all C++20 algorithms,N/A,N/A,✅ -C++23,`find_last `_,Unassigned,No patch yet,Not started -C++23,`find_last_if `_,Unassigned,No patch yet,Not started -C++23,`find_last_if_not `_,Unassigned,No patch yet,Not started +C++23,`find_last `_,Nicole Mazzuca,`#99312 `_,Complete +C++23,`find_last_if `_,Nicole Mazzuca,`#99312 `_,Complete +C++23,`find_last_if_not `_,Nicole Mazzuca,`#99312 `_,Complete C++23,`starts_with `_,Zijun Zhao,`D150735 `_,Complete C++23,`ends_with `_,Zijun Zhao, `D150831 `_,Complete C++23,`shift_left `_,Unassigned,No patch yet,Not started diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt index fa6736dc11e66a..1a4d9c7070f145 100644 --- a/libcxx/include/CMakeLists.txt +++ b/libcxx/include/CMakeLists.txt @@ -97,6 +97,7 @@ set(files __algorithm/ranges_find_first_of.h __algorithm/ranges_find_if.h __algorithm/ranges_find_if_not.h + __algorithm/ranges_find_last.h __algorithm/ranges_for_each.h __algorithm/ranges_for_each_n.h __algorithm/ranges_generate.h diff --git a/libcxx/include/__algorithm/ranges_find_last.h b/libcxx/include/__algorithm/ranges_find_last.h new file mode 100644 index 00000000000000..95f7e77b8ccbea --- /dev/null +++ b/libcxx/include/__algorithm/ranges_find_last.h @@ -0,0 +1,175 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +#ifndef _LIBCPP___ALGORITHM_RANGES_FIND_LAST_H +#define _LIBCPP___ALGORITHM_RANGES_FIND_LAST_H + +#include <__config> +#include <__functional/identity.h> +#include <__functional/invoke.h> +#include <__functional/ranges_operations.h> +#include <__iterator/concepts.h> +#include <__iterator/indirectly_comparable.h> +#include <__iterator/next.h> +#include <__iterator/prev.h> +#include <__iterator/projected.h> +#include <__ranges/access.h> +#include <__ranges/concepts.h> +#include <__ranges/subrange.h> +#include <__utility/move.h> + +#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER) +# pragma GCC system_header +#endif + +_LIBCPP_PUSH_MACROS +#include <__undef_macros> + +#if _LIBCPP_STD_VER >= 23 + +_LIBCPP_BEGIN_NAMESPACE_STD + +namespace ranges { + +template +_LIBCPP_HIDE_FROM_ABI constexpr subrange<_Iter> +__find_last_impl(_Iter __first, _Sent __last, _Pred __pred, _Proj& __proj) { + if (__first == __last) { + return subrange<_Iter>(__first, __first); + } + + if constexpr (bidirectional_iterator<_Iter>) { + auto __last_it = ranges::next(__first, __last); + for (auto __it = ranges::prev(__last_it); __it != __first; --__it) { + if (__pred(std::invoke(__proj, *__it))) { + return subrange<_Iter>(std::move(__it), std::move(__last_it)); + } + } + if (__pred(std::invoke(__proj, *__first))) { + return subrange<_Iter>(std::move(__first), std::move(__last_it)); + } + return subrange<_Iter>(__last_it, __last_it); + } else { + bool __found = false; + _Iter __found_it; + for (; __first != __last; ++__first) { + if (__pred(std::invoke(__proj, *__first))) { + __found = true; + __found_it = __first; + } + } + + if (__found) { + return subrange<_Iter>(std::move(__found_it), std::move(__first)); + } else { + return subrange<_Iter>(__first, __first); + } + } +} + +namespace __find_last { +struct __fn { + template + struct __op { + const _Type& __value; + template + _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator()(_Elem&& __elem) const { + return std::forward<_Elem>(__elem) == __value; + } + }; + + template _Sent, class _Type, class _Proj = identity> + requires indirect_binary_predicate, const _Type*> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static subrange<_Iter> + operator()(_Iter __first, _Sent __last, const _Type& __value, _Proj __proj = {}) { + return ranges::__find_last_impl(std::move(__first), std::move(__last), __op<_Type>{__value}, __proj); + } + + template + requires indirect_binary_predicate, _Proj>, const _Type*> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static borrowed_subrange_t<_Range> + operator()(_Range&& __range, const _Type& __value, _Proj __proj = {}) { + return ranges::__find_last_impl(ranges::begin(__range), ranges::end(__range), __op<_Type>{__value}, __proj); + } +}; +} // namespace __find_last + +namespace __find_last_if { +struct __fn { + template + struct __op { + _Pred& __pred; + template + _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator()(_Elem&& __elem) const { + return std::invoke(__pred, std::forward<_Elem>(__elem)); + } + }; + + template _Sent, + class _Proj = identity, + indirect_unary_predicate> _Pred> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static subrange<_Iter> + operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) { + return ranges::__find_last_impl(std::move(__first), std::move(__last), __op<_Pred>{__pred}, __proj); + } + + template , _Proj>> _Pred> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static borrowed_subrange_t<_Range> + operator()(_Range&& __range, _Pred __pred, _Proj __proj = {}) { + return ranges::__find_last_impl(ranges::begin(__range), ranges::end(__range), __op<_Pred>{__pred}, __proj); + } +}; +} // namespace __find_last_if + +namespace __find_last_if_not { +struct __fn { + template + struct __op { + _Pred& __pred; + template + _LIBCPP_HIDE_FROM_ABI constexpr decltype(auto) operator()(_Elem&& __elem) const { + return !std::invoke(__pred, std::forward<_Elem>(__elem)); + } + }; + + template _Sent, + class _Proj = identity, + indirect_unary_predicate> _Pred> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static subrange<_Iter> + operator()(_Iter __first, _Sent __last, _Pred __pred, _Proj __proj = {}) { + return ranges::__find_last_impl(std::move(__first), std::move(__last), __op<_Pred>{__pred}, __proj); + } + + template , _Proj>> _Pred> + [[nodiscard]] _LIBCPP_HIDE_FROM_ABI constexpr static borrowed_subrange_t<_Range> + operator()(_Range&& __range, _Pred __pred, _Proj __proj = {}) { + return ranges::__find_last_impl(ranges::begin(__range), ranges::end(__range), __op<_Pred>{__pred}, __proj); + } +}; +} // namespace __find_last_if_not + +inline namespace __cpo { +inline constexpr auto find_last = __find_last::__fn{}; +inline constexpr auto find_last_if = __find_last_if::__fn{}; +inline constexpr auto find_last_if_not = __find_last_if_not::__fn{}; +} // namespace __cpo +} // namespace ranges + +_LIBCPP_END_NAMESPACE_STD + +#endif // _LIBCPP_STD_VER >= 23 + +_LIBCPP_POP_MACROS + +#endif // _LIBCPP___ALGORITHM_RANGES_FIND_LAST_H diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm index a522a60f1b5511..698e6f5cb7ad1f 100644 --- a/libcxx/include/algorithm +++ b/libcxx/include/algorithm @@ -102,6 +102,31 @@ namespace ranges { constexpr borrowed_iterator_t find_if_not(R&& r, Pred pred, Proj proj = {}); // since C++20 + template S, class T, class Proj = identity> + requires indirect_binary_predicate, const T*> + constexpr subrange find_last(I first, S last, const T& value, Proj proj = {}); // since C++23 + + template + requires + indirect_binary_predicate, Proj>, const T*> + constexpr borrowed_subrange_t find_last(R&& r, const T& value, Proj proj = {}); // since C++23 + + template S, class Proj = identity, + indirect_unary_predicate> Pred> + constexpr subrange find_last_if(I first, S last, Pred pred, Proj proj = {}); // since C++23 + + template, Proj>> Pred> + constexpr borrowed_subrange_t find_last_if(R&& r, Pred pred, Proj proj = {}); // since C++23 + + template S, class Proj = identity, + indirect_unary_predicate> Pred> + constexpr subrange find_last_if_not(I first, S last, Pred pred, Proj proj = {}); // since C++23 + + template, Proj>> Pred> + constexpr borrowed_subrange_t find_last_if_not(R&& r, Pred pred, Proj proj = {}); // since C++23 + template> Comp = ranges::less> constexpr const T& min(const T& a, const T& b, Comp comp = {}, Proj proj = {}); // since C++20 @@ -1989,6 +2014,7 @@ template # include <__algorithm/fold.h> # include <__algorithm/ranges_contains_subrange.h> # include <__algorithm/ranges_ends_with.h> +# include <__algorithm/ranges_find_last.h> # include <__algorithm/ranges_starts_with.h> #endif // _LIBCPP_STD_VER >= 23 diff --git a/libcxx/include/module.modulemap b/libcxx/include/module.modulemap index 43ab9c6987c842..3443fbc3347a3e 100644 --- a/libcxx/include/module.modulemap +++ b/libcxx/include/module.modulemap @@ -764,6 +764,7 @@ module std_private_algorithm_ranges_find_end [system module std_private_algorithm_ranges_find_first_of [system] { header "__algorithm/ranges_find_first_of.h" } module std_private_algorithm_ranges_find_if [system] { header "__algorithm/ranges_find_if.h" } module std_private_algorithm_ranges_find_if_not [system] { header "__algorithm/ranges_find_if_not.h" } +module std_private_algorithm_ranges_find_last [system] { header "__algorithm/ranges_find_last.h" } module std_private_algorithm_ranges_for_each [system] { header "__algorithm/ranges_for_each.h" export std_private_algorithm_in_fun_result diff --git a/libcxx/include/version b/libcxx/include/version index 68aa88a48fa8fa..40548098a92d66 100644 --- a/libcxx/include/version +++ b/libcxx/include/version @@ -190,6 +190,7 @@ __cpp_lib_ranges_chunk 202202L __cpp_lib_ranges_chunk_by 202202L __cpp_lib_ranges_concat 202403L __cpp_lib_ranges_contains 202207L +__cpp_lib_ranges_find_last 202207L __cpp_lib_ranges_iota 202202L __cpp_lib_ranges_join_with 202202L __cpp_lib_ranges_repeat 202207L @@ -484,6 +485,7 @@ __cpp_lib_void_t 201411L // # define __cpp_lib_ranges_chunk 202202L # define __cpp_lib_ranges_chunk_by 202202L # define __cpp_lib_ranges_contains 202207L +# define __cpp_lib_ranges_find_last 202207L // # define __cpp_lib_ranges_iota 202202L // # define __cpp_lib_ranges_join_with 202202L # define __cpp_lib_ranges_repeat 202207L diff --git a/libcxx/modules/std/algorithm.inc b/libcxx/modules/std/algorithm.inc index e7796bfa26af81..3c2139cd64ee49 100644 --- a/libcxx/modules/std/algorithm.inc +++ b/libcxx/modules/std/algorithm.inc @@ -77,13 +77,14 @@ export namespace std { using std::ranges::find_if_not; } // namespace ranges +#if _LIBCPP_STD_VER >= 23 + // [alg.find.last], find last namespace ranges { -#if 0 using std::ranges::find_last; using std::ranges::find_last_if; using std::ranges::find_last_if_not; -#endif } // namespace ranges +#endif // [alg.find.end], find end using std::find_end; diff --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp index 1a6c0d11460c5a..6bac7deadfa6ed 100644 --- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp +++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp @@ -119,6 +119,16 @@ constexpr bool all_the_algorithms() (void)std::ranges::find_if(a, UnaryTrue(&copies)); assert(copies == 0); (void)std::ranges::find_if_not(first, last, UnaryTrue(&copies)); assert(copies == 0); (void)std::ranges::find_if_not(a, UnaryTrue(&copies)); assert(copies == 0); +#if TEST_STD_VER >= 23 + (void)std::ranges::find_last_if(first, last, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::ranges::find_last_if(a, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::ranges::find_last_if_not(first, last, UnaryTrue(&copies)); + assert(copies == 0); + (void)std::ranges::find_last_if_not(a, UnaryTrue(&copies)); + assert(copies == 0); +#endif (void)std::ranges::for_each(first, last, UnaryVoid(&copies)); assert(copies == 1); copies = 0; (void)std::ranges::for_each(a, UnaryVoid(&copies)); assert(copies == 1); copies = 0; (void)std::ranges::for_each_n(first, count, UnaryVoid(&copies)); assert(copies == 1); copies = 0; diff --git a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp index 71823d9afc1a4b..bc71150c9e2d86 100644 --- a/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp +++ b/libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp @@ -115,6 +115,20 @@ constexpr bool all_the_algorithms() (void)std::ranges::find_if(a, UnaryTrue(), Proj(&copies)); assert(copies == 0); (void)std::ranges::find_if_not(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 0); (void)std::ranges::find_if_not(a, UnaryTrue(), Proj(&copies)); assert(copies == 0); +#if TEST_STD_VER >= 23 + (void)std::ranges::find_last(first, last, value, Proj(&copies)); + assert(copies == 0); + (void)std::ranges::find_last(a, value, Proj(&copies)); + assert(copies == 0); + (void)std::ranges::find_last_if(first, last, UnaryTrue(), Proj(&copies)); + assert(copies == 0); + (void)std::ranges::find_last_if(a, UnaryTrue(), Proj(&copies)); + assert(copies == 0); + (void)std::ranges::find_last_if_not(first, last, UnaryTrue(), Proj(&copies)); + assert(copies == 0); + (void)std::ranges::find_last_if_not(a, UnaryTrue(), Proj(&copies)); + assert(copies == 0); +#endif (void)std::ranges::for_each(first, last, UnaryVoid(), Proj(&copies)); assert(copies == 0); (void)std::ranges::for_each(a, UnaryVoid(), Proj(&copies)); assert(copies == 0); (void)std::ranges::for_each_n(first, count, UnaryVoid(), Proj(&copies)); assert(copies == 0); diff --git a/libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp b/libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp index b36a426082ccb4..14febc12a8a2d9 100644 --- a/libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp +++ b/libcxx/test/libcxx/diagnostics/algorithm.nodiscard.verify.cpp @@ -372,6 +372,18 @@ void test() { // expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::ranges::contains_subrange(iter, iter, iter, iter); // expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::ranges::find_last_if_not(iter, iter, pred); + // expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::ranges::find_last_if_not(range, pred); + // expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::ranges::find_last_if(iter, iter, pred); + // expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::ranges::find_last_if(range, pred); + // expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::ranges::find_last(iter, iter, 1); + // expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}} + std::ranges::find_last(range, 1); + // expected-warning@-1 {{ignoring return value of function declared with 'nodiscard' attribute}} std::ranges::fold_left(range, 0, std::plus()); // expected-warning@-1{{ignoring return value of function declared with 'nodiscard' attribute}} std::ranges::fold_left(iter, iter, 0, std::plus()); diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.last/ranges.find_last.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.last/ranges.find_last.pass.cpp new file mode 100644 index 00000000000000..2a2b12fb2c288d --- /dev/null +++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find.last/ranges.find_last.pass.cpp @@ -0,0 +1,204 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20 + +// ADDITIONAL_COMPILE_FLAGS(gcc-style-warnings): -Wno-sign-compare +// MSVC warning C4242: 'argument': conversion from 'const _Ty' to 'ElementT', possible loss of data +// MSVC warning C4244: 'argument': conversion from 'const _Ty' to 'ElementT', possible loss of data +// ADDITIONAL_COMPILE_FLAGS(cl-style-warnings): /wd4242 /wd4244 + +// template S, class T, class Proj = identity> +// requires indirect_binary_predicate, const T*> +// constexpr subrange ranges::find_last(I first, S last, const T& value, Proj proj = {}); +// template +// requires indirect_binary_predicate, Proj>, const T*> +// constexpr borrowed_subrange_t ranges::find_last(R&& r, const T& value, Proj proj = {}); + +#include +#include +#include +#include +#include + +#include "almost_satisfies_types.h" +#include "test_iterators.h" + +struct NotEqualityComparable {}; + +template +concept HasFindLastIt = requires(It it, Sent sent) { std::ranges::find_last(it, sent, *it); }; +static_assert(HasFindLastIt); +static_assert(HasFindLastIt>); +static_assert(!HasFindLastIt>); +static_assert(!HasFindLastIt); +static_assert(!HasFindLastIt); +static_assert(!HasFindLastIt); +static_assert(!HasFindLastIt, SentinelForNotSemiregular>); +static_assert(!HasFindLastIt, InputRangeNotSentinelEqualityComparableWith>); + +static_assert(!HasFindLastIt); +static_assert(!HasFindLastIt); + +template +concept HasFindLastR = requires(Range r) { std::ranges::find_last(r, ValT{}); }; +static_assert(HasFindLastR, int>); +static_assert(!HasFindLastR); +static_assert(!HasFindLastR, NotEqualityComparable>); +static_assert(!HasFindLastR); +static_assert(!HasFindLastR); +static_assert(!HasFindLastR); +static_assert(!HasFindLastR); + +template +constexpr void test_iterators() { + using ValueT = std::iter_value_t; + auto make_range = [](auto& a) { + return std::ranges::subrange(It(std::ranges::begin(a)), Sent(It(std::ranges::end(a)))); + }; + { // simple test + { + ValueT a[] = {1, 2, 3, 4}; + + std::same_as> auto ret = std::ranges::find_last(It(a), Sent(It(a + 4)), 2); + assert(base(ret.begin()) == a + 1); + assert(*ret.begin() == 2); + } + { + ValueT a[] = {1, 2, 3, 4}; + + std::same_as> auto ret = std::ranges::find_last(make_range(a), 2); + assert(base(ret.begin()) == a + 1); + assert(*ret.begin() == 2); + } + } + + { // check that an empty range works + { + std::array a = {}; + + auto ret = std::ranges::find_last(It(a.data()), Sent(It(a.data())), 1).begin(); + assert(ret == It(a.data())); + } + { + std::array a = {}; + + auto ret = std::ranges::find_last(make_range(a), 1).begin(); + assert(ret == It(a.begin())); + } + } + + { // check that last is returned with no match + { + ValueT a[] = {1, 1, 1}; + + auto ret = std::ranges::find_last(It(a), Sent(It(a + 3)), 0).begin(); + assert(ret == It(a + 3)); + } + { + ValueT a[] = {1, 1, 1}; + + auto ret = std::ranges::find_last(make_range(a), 0).begin(); + assert(ret == It(a + 3)); + } + } +} + +template