-
Notifications
You must be signed in to change notification settings - Fork 11.9k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[libcxx] patch for implementing ranges::find_last #67270
Conversation
@llvm/pr-subscribers-libcxx ChangesFollow up the discussion from https://reviews.llvm.org/D136522 Patch is 59.02 KiB, truncated to 20.00 KiB below, full version: https://github.com/llvm/llvm-project/pull/67270.diff 20 Files Affected:
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index e1b4172b22c53da..e2224a5a0ce5cc6 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -322,6 +322,8 @@ Status
--------------------------------------------------- -----------------
``__cpp_lib_expected`` ``202211L``
--------------------------------------------------- -----------------
+ ``__cpp_lib_find_last`` ``202207L``
+ ------------------------------------------------- -------------------
``__cpp_lib_format_ranges`` ``202207L``
--------------------------------------------------- -----------------
``__cpp_lib_formatters`` *unimplemented*
diff --git a/libcxx/docs/ReleaseNotes/18.rst b/libcxx/docs/ReleaseNotes/18.rst
index 8869d0b07d39f74..d6a57d8cac91a99 100644
--- a/libcxx/docs/ReleaseNotes/18.rst
+++ b/libcxx/docs/ReleaseNotes/18.rst
@@ -52,7 +52,7 @@ Implemented Papers
- P2697R1 - Interfacing ``bitset`` with ``string_view``
- P2443R1 - ``views::chunk_by``
- P2538R1 - ADL-proof ``std::projected``
-
+- P1223R5 - ``find_last``
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 9cb49fd5176ea5f..216aca3a5f2b3c8 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -55,7 +55,7 @@
"`P0429R9 <https://wg21.link/P0429R9>`__","LWG","A Standard ``flat_map``","July 2022","",""
"`P1169R4 <https://wg21.link/P1169R4>`__","LWG","``static operator()``","July 2022","|Complete|","16.0"
"`P1222R4 <https://wg21.link/P1222R4>`__","LWG","A Standard ``flat_set``","July 2022","",""
-"`P1223R5 <https://wg21.link/P1223R5>`__","LWG","``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()``","July 2022","","","|ranges|"
+"`P1223R5 <https://wg21.link/P1223R5>`__","LWG","``ranges::find_last()``, ``ranges::find_last_if()``, and ``ranges::find_last_if_not()``","July 2022","|Complete|","18.0","|ranges|"
"`P1467R9 <https://wg21.link/P1467R9>`__","LWG","Extended ``floating-point`` types and standard names","July 2022","",""
"`P1642R11 <https://wg21.link/P1642R11>`__","LWG","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","July 2022","",""
"`P1899R3 <https://wg21.link/P1899R3>`__","LWG","``stride_view``","July 2022","","","|ranges|"
diff --git a/libcxx/include/CMakeLists.txt b/libcxx/include/CMakeLists.txt
index 2ec755236dbaee2..45083070acd4751 100644
--- a/libcxx/include/CMakeLists.txt
+++ b/libcxx/include/CMakeLists.txt
@@ -120,6 +120,9 @@ 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_find_last_if.h
+ __algorithm/ranges_find_last_if_not.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 000000000000000..81c2caef413bbab
--- /dev/null
+++ b/libcxx/include/__algorithm/ranges_find_last.h
@@ -0,0 +1,62 @@
+//===----------------------------------------------------------------------===//
+//
+// 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 <__algorithm/ranges_find_last_if.h>
+#include <__config>
+#include <__functional/identity.h>
+#include <__iterator/concepts.h>
+#include <__functional/ranges_operations.h>
+#include <__iterator/projected.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/dangling.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 23
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace ranges {
+namespace __find_last {
+struct __fn {
+ template <forward_iterator _Ip, sentinel_for<_Ip> _Sp, class _Tp, class _Proj = identity>
+ requires indirect_binary_predicate <ranges::equal_to, projected<_Ip, _Proj>, const _Tp*>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Ip>
+ operator()(_Ip __first, _Sp __last, const _Tp& __value, _Proj __proj = {}) const {
+ auto __pred = [&](auto&& __e) { return std::forward<decltype(__e)>(__e) == __value; };
+ return ranges::__find_last_if_impl(std::move(__first), std::move(__last), __pred, __proj);
+ }
+
+ template <forward_range _Rp, class _Tp, class _Proj = identity>
+ requires indirect_binary_predicate <ranges::equal_to, projected<iterator_t<_Rp>, _Proj>, const _Tp*>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Rp>
+ operator()(_Rp&& __r, const _Tp& __value, _Proj __proj = {}) const {
+ auto __pred = [&](auto&& __e) { return std::forward<decltype(__e)>(__e) == __value; };
+ return ranges::__find_last_if_impl(ranges::begin(__r), ranges::end(__r), __pred, __proj);
+ }
+};
+} // namespace __find_last
+
+inline namespace __cpo {
+inline constexpr auto find_last = __find_last::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 23
+
+#endif // _LIBCPP___ALGORITHM_RANGES_FIND_LAST_H
diff --git a/libcxx/include/__algorithm/ranges_find_last_if.h b/libcxx/include/__algorithm/ranges_find_last_if.h
new file mode 100644
index 000000000000000..894144fd6f2ea8e
--- /dev/null
+++ b/libcxx/include/__algorithm/ranges_find_last_if.h
@@ -0,0 +1,77 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_IF_H
+#define _LIBCPP___ALGORITHM_RANGES_FIND_LAST_IF_H
+
+#include <__config>
+#include <__functional/identity.h>
+#include <__functional/invoke.h>
+#include <__iterator/concepts.h>
+#include <__iterator/projected.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/dangling.h>
+#include <__ranges/range_adaptor.h>
+#include <__ranges/subrange.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 23
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace ranges {
+
+template <class _Ip, class _Sp, class _Pred, class _Proj>
+_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr subrange<_Ip>
+__find_last_if_impl(_Ip __first, _Sp __last, _Pred __pred, _Proj __proj) {
+ _Ip __result = __last;
+ while (__first != __last) {
+ if (std::invoke(__pred, std::invoke(__proj, *__first))) {
+ __result = __first;
+ }
+ ++__first;
+ }
+ return {__result, __last};
+}
+
+namespace __find_last_if {
+struct __fn {
+ template <forward_range _Rp,
+ class _Proj = identity,
+ indirect_unary_predicate<projected<iterator_t<_Rp>, _Proj>> _Pred>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Rp>
+ operator()(_Rp&& __r, _Pred __pred, _Proj __proj = {}) const {
+ return ranges::__find_last_if_impl(ranges::begin(__r), ranges::end(__r), __pred, __proj);
+ }
+
+ template <forward_iterator _Ip,
+ sentinel_for<_Ip> _Sp,
+ class _Proj = identity,
+ indirect_unary_predicate<projected<_Ip, _Proj>> _Pred>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Ip>
+ operator()(_Ip __first, _Sp __last, _Pred __pred, _Proj __proj = {}) const {
+ return ranges::__find_last_if_impl(std::move(__first), std::move(__last), __pred, __proj);
+ }
+};
+} // namespace __find_last_if
+
+inline namespace __cpo {
+inline constexpr auto find_last_if = __find_last_if::__fn{};
+} // namespace __cpo
+} // namespace ranges
+
+_LIBCPP_END_NAMESPACE_STD
+
+#endif // _LIBCPP_STD_VER >= 23
+
+#endif // _LIBCPP___ALGORITHM_RANGES_FIND_LAST_IF_H
diff --git a/libcxx/include/__algorithm/ranges_find_last_if_not.h b/libcxx/include/__algorithm/ranges_find_last_if_not.h
new file mode 100644
index 000000000000000..e1b29d8f4870e1d
--- /dev/null
+++ b/libcxx/include/__algorithm/ranges_find_last_if_not.h
@@ -0,0 +1,69 @@
+//===----------------------------------------------------------------------===//
+//
+// 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_IF_NOT_H
+#define _LIBCPP___ALGORITHM_RANGES_FIND_LAST_IF_NOT_H
+
+#include <__algorithm/ranges_find_last_if.h>
+#include <__config>
+#include <__functional/identity.h>
+#include <__functional/invoke.h>
+#include <__iterator/concepts.h>
+#include <__iterator/projected.h>
+#include <__ranges/access.h>
+#include <__ranges/concepts.h>
+#include <__ranges/dangling.h>
+#include <__utility/forward.h>
+#include <__utility/move.h>
+
+#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
+# pragma GCC system_header
+#endif
+
+#if _LIBCPP_STD_VER >= 23
+
+_LIBCPP_BEGIN_NAMESPACE_STD
+
+namespace ranges {
+namespace __find_last_if_not {
+struct __fn {
+ static constexpr auto __make_negate_pred = [](auto&& __pred) {
+ return [&__pred](auto&& __e) { return !std::invoke(__pred, std::forward<decltype(__e)>(__e)); };
+};
+
+ template <forward_iterator _Ip,
+ sentinel_for<_Ip> _Sp,
+ class _Proj = identity,
+ indirect_unary_predicate<projected<_Ip, _Proj>> _Pred>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Ip>
+ operator()(_Ip __first, _Sp __last, _Pred __pred, _Proj __proj = {}) const {
+ auto __negate_pred = __make_negate_pred(__pred);
+ return ranges::__find_last_if_impl(std::move(__first), std::move(__last), __negate_pred, __proj);
+ }
+
+ template <forward_range _Rp,
+ class _Proj = identity,
+ indirect_unary_predicate<projected<iterator_t<_Rp>, _Proj>> _Pred>
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Rp>
+ operator()(_Rp&& __r, _Pred __pred, _Proj __proj = {}) const {
+ auto __negate_pred = __make_negate_pred(__pred);
+ return ranges::__find_last_if_impl(ranges::begin(__r), ranges::end(__r), __negate_pred, __proj);
+ }
+};
+} // namespace __find_last_if_not
+
+inline namespace __cpo {
+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
+
+#endif // _LIBCPP___ALGORITHM_RANGES_FIND_LAST_IF_NOT_H
diff --git a/libcxx/include/algorithm b/libcxx/include/algorithm
index 69ba9537dda6984..cfc4c09007c5b7f 100644
--- a/libcxx/include/algorithm
+++ b/libcxx/include/algorithm
@@ -99,6 +99,36 @@ namespace ranges {
constexpr borrowed_iterator_t<R>
find_if_not(R&& r, Pred pred, Proj proj = {}); // since C++20
+ template<forward_iterator I, sentinel_for<I> S, class T, class Proj = identity>
+ requires indirect_binary_predicate<ranges::equal_to, projected<I, Proj>, const T*>
+ constexpr subrange<I>
+ ranges::find_last(I first, S last, const T& value, Proj proj = {}); // since C++23
+
+ template<forward_range R, class T, class Proj = identity>
+ requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<R>, Proj>, const T*>
+ constexpr borrowed_subrange_t<R>
+ ranges::find_last(R&& r, const T& value, Proj proj = {}); //since C++23
+
+ template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
+ indirect_unary_predicate<projected<I, Proj>> Pred>
+ constexpr subrange<I>
+ ranges::find_last_if(I first, S last, Pred pred, Proj proj = {}); // since C++23
+
+ template<forward_range R, class Proj = identity,
+ indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
+ constexpr borrowed_subrange_t<R>
+ ranges::find_last_if(R&& r, Pred pred, Proj proj = {}); // since C++23
+
+ template<forward_iterator I, sentinel_for<I> S, class Proj = identity,
+ indirect_unary_predicate<projected<I, Proj>> Pred>
+ constexpr subrange<I>
+ ranges::find_last_if_not(I first, S last, Pred pred, Proj proj = {}); // since C++23
+
+ template<forward_range R, class Proj = identity,
+ indirect_unary_predicate<projected<iterator_t<R>, Proj>> Pred>
+ constexpr borrowed_subrange_t<R>
+ ranges::find_last_if_not(R&& r, Pred pred, Proj proj = {}); // since C++23
+
template<class T, class Proj = identity,
indirect_strict_weak_order<projected<const T*, Proj>> Comp = ranges::less>
constexpr const T& min(const T& a, const T& b, Comp comp = {}, Proj proj = {}); // since C++20
@@ -1859,6 +1889,9 @@ template <class BidirectionalIterator, class Compare>
#include <__algorithm/ranges_find_first_of.h>
#include <__algorithm/ranges_find_if.h>
#include <__algorithm/ranges_find_if_not.h>
+#include <__algorithm/ranges_find_last.h>
+#include <__algorithm/ranges_find_last_if.h>
+#include <__algorithm/ranges_find_last_if_not.h>
#include <__algorithm/ranges_for_each.h>
#include <__algorithm/ranges_for_each_n.h>
#include <__algorithm/ranges_generate.h>
diff --git a/libcxx/include/module.modulemap.in b/libcxx/include/module.modulemap.in
index 6d9bb8653fcb5e9..eb42d6f59b138f4 100644
--- a/libcxx/include/module.modulemap.in
+++ b/libcxx/include/module.modulemap.in
@@ -842,6 +842,18 @@ 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"
+ export std_private_ranges_subrange
+}
+module std_private_algorithm_ranges_find_last_if [system] {
+ header "__algorithm/ranges_find_last_if.h"
+ export std_private_ranges_subrange
+}
+module std_private_algorithm_ranges_find_last_if_not [system] {
+ header "__algorithm/ranges_find_last_if_not.h"
+ export std_private_ranges_subrange
+}
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 e5a995366a7aa48..2bacf36a6295cb7 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -90,6 +90,7 @@ __cpp_lib_execution 201902L <execution>
201603L // C++17
__cpp_lib_expected 202211L <expected>
__cpp_lib_filesystem 201703L <filesystem>
+__cpp_lib_find_last 202207L <algorithm>
__cpp_lib_format 202106L <format>
__cpp_lib_format_ranges 202207L <format>
__cpp_lib_formatters 202302L <stacktrace> <thread>
@@ -422,6 +423,7 @@ __cpp_lib_within_lifetime 202306L <type_traits>
# define __cpp_lib_constexpr_memory 202202L
# define __cpp_lib_constexpr_typeinfo 202106L
# define __cpp_lib_expected 202211L
+# define __cpp_lib_find_last 202207L
# define __cpp_lib_format_ranges 202207L
// # define __cpp_lib_formatters 202302L
# define __cpp_lib_forward_like 202207L
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 eece9833f8a6357..85d5ba35d8184ba 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
@@ -118,6 +118,10 @@ 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);
+ (void)std::ranges::find_last_if(first, last, UnaryTrue(&copies)); assert(copies == 1); copies = 0;
+ (void)std::ranges::find_last_if(a, UnaryTrue(&copies)); assert(copies == 1); 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);
(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 afbbc224ea8644c..d3939881ab21ff6 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
@@ -104,6 +104,12 @@ 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);
+ (void)std::ranges::find_last(first, last, value, Proj(&copies)); assert(copies == 1); copies = 0;
+ (void)std::ranges::find_last(a, value, Proj(&copies)); assert(copies == 1); copies = 0;
+ (void)std::ranges::find_last_if(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 1); copies = 0;
+ (void)std::ranges::find_last_if(a, UnaryTrue(), Proj(&copies)); assert(copies == 1); copies = 0;
+ (void)std::ranges::find_last_if_not(first, last, UnaryTrue(), Proj(&copies)); assert(copies == 1); copies = 0;
+ (void)std::ranges::find_last_if_not(a, UnaryTrue(), Proj(&copies)); assert(copies == 1); copies = 0;
(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/ranges.nodiscard_extensions.verify.cpp b/libcxx/test/libcxx/diagnostics/ranges.nodiscard_extensions.verify.cpp
index 77ac5f3f7790357..7528acdae28dfb3 100644
--- a/libcxx/test/libcxx/diagnostics/ranges.nodiscard_extensions.verify.cpp
+++ b/libcxx/test/libcxx/diagnostics/ranges.nodiscard_extensions.verify.cpp
@@ -41,6 +41,10 @@ void test() {
std::ranges::find_if_not(iter, iter, pred); // expected-warning {{ignoring...
[truncated]
|
✅ With the latest revision this PR passed the Python code formatter. |
You can test this locally with the following command:git-clang-format --diff 0d7c340c2cc89b71d398479c9adcac77f0336c6e d0fe8a81f52d766fbe62e6e3de3dd6f770d31c8e -- libcxx/include/__algorithm/ranges_find_last.h libcxx/include/__algorithm/ranges_find_last_if.h libcxx/include/__algorithm/ranges_find_last_if_not.h libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last.pass.cpp libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last_if.pass.cpp libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last_if_not.pass.cpp libcxx/include/algorithm libcxx/include/version libcxx/test/libcxx/algorithms/ranges_robust_against_copying_comparators.pass.cpp libcxx/test/libcxx/algorithms/ranges_robust_against_copying_projections.pass.cpp libcxx/test/libcxx/diagnostics/ranges.nodiscard_extensions.verify.cpp libcxx/test/std/language.support/support.limits/support.limits.general/algorithm.version.compile.pass.cpp libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp libcxx/test/std/library/description/conventions/customization.point.object/niebloid.compile.pass.cpp libcxx/test/support/almost_satisfies_types.h View the diff from clang-format here.diff --git a/libcxx/include/__algorithm/ranges_find_last.h b/libcxx/include/__algorithm/ranges_find_last.h
index 15c5380a983..c46f3ab360b 100644
--- a/libcxx/include/__algorithm/ranges_find_last.h
+++ b/libcxx/include/__algorithm/ranges_find_last.h
@@ -33,17 +33,17 @@ namespace ranges {
namespace __find_last {
struct __fn {
template <forward_iterator _Ip, sentinel_for<_Ip> _Sp, class _Tp, class _Proj = identity>
- requires indirect_binary_predicate < ranges::equal_to, projected<_Ip, _Proj>,
- const _Tp* > _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Ip>
- operator()(_Ip __first, _Sp __last, const _Tp& __value, _Proj __proj = {}) const {
+ requires indirect_binary_predicate< ranges::equal_to, projected<_Ip, _Proj>, const _Tp* >
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Ip>
+ operator()(_Ip __first, _Sp __last, const _Tp& __value, _Proj __proj = {}) const {
auto __pred = [&](auto&& __e) { return std::forward<decltype(__e)>(__e) == __value; };
return ranges::__find_last_if_impl(std::move(__first), std::move(__last), __pred, __proj);
}
template <forward_range _Rp, class _Tp, class _Proj = identity>
- requires indirect_binary_predicate < ranges::equal_to, projected<iterator_t<_Rp>, _Proj>,
- const _Tp* > _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Rp>
- operator()(_Rp&& __r, const _Tp& __value, _Proj __proj = {}) const {
+ requires indirect_binary_predicate< ranges::equal_to, projected<iterator_t<_Rp>, _Proj>, const _Tp* >
+ _LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Rp>
+ operator()(_Rp&& __r, const _Tp& __value, _Proj __proj = {}) const {
auto __pred = [&](auto&& __e) { return std::forward<decltype(__e)>(__e) == __value; };
return ranges::__find_last_if_impl(ranges::begin(__r), ranges::end(__r), __pred, __proj);
}
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last.pass.cpp
index 3ec1f7fed1a..21f87b51ad7 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last.pass.cpp
@@ -31,9 +31,7 @@
struct NotEqualityComparable {};
template <class It, class Sent = It>
-concept HasFindIt = requires(It it, Sent sent) {
- std::ranges::find_last(it, sent, *it);
-};
+concept HasFindIt = requires(It it, Sent sent) { std::ranges::find_last(it, sent, *it); };
static_assert(HasFindIt<int*>);
static_assert(!HasFindIt<NotEqualityComparable*>);
static_assert(!HasFindIt<InputIteratorNotDerivedFrom>);
@@ -46,9 +44,7 @@ static_assert(!HasFindIt<int*, int>);
static_assert(!HasFindIt<int, int*>);
template <class Range, class ValT>
-concept HasFindR = requires(Range r) {
- std::ranges::find_last(r, ValT{});
-};
+concept HasFindR = requires(Range r) { std::ranges::find_last(r, ValT{}); };
static_assert(HasFindR<std::array<int, 1>, int>);
static_assert(!HasFindR<int, int>);
@@ -79,205 +75,213 @@ struct NonConstComparableRValue {
};
constexpr bool test() {
- {// check that projections are used properly and called with the reference to the element the iterator is pointing to
- {int a[] = {1, 2, 3, 4};
- std::same_as<std::ranges::subrange<int*>> auto ret =
- std::ranges::find_last(a, a + 4, a + 3, [](int& i) { return &i; });
- assert(ret.data() == a + 3);
-}
-
-{
- int a[] = {1, 2, 3, 4};
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a, a + 3, [](int& i) { return &i; });
- assert(ret.data() == a + 3);
-}
-}
-
-{// check that the end element is returned
-
- {struct S{int comp;
-int other;
-}
-;
-
-S a[] = {{0, 0}, {0, 2}, {0, 1}};
-std::same_as<std::ranges::borrowed_subrange_t<S (&)[3]>> auto ret = std::ranges::find_last(a, 0, &S::comp);
-assert(ret.data() == a + 2);
-assert(ret.data()->comp == 0);
-assert(ret.data()->other == 1);
-}
-
-{
- struct S {
- int comp;
- int other;
- };
-
- S a[] = {{0, 0}, {0, 2}, {0, 1}};
- std::same_as<std::ranges::subrange<S*>> auto ret = std::ranges::find_last(a, a + 3, 0, &S::comp);
- assert(ret.data() == a + 2);
- assert(ret.data()->comp == 0);
- assert(ret.data()->other == 1);
-}
-}
-
-{// check that end + 1 iterator is returned with no match
-
- {int a[] = {1, 1, 1};
-std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a, a + 3, 0);
-assert(ret.data() == a + 3);
-}
-
-{
- int a[] = {1, 1, 1};
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a, 0);
- assert(ret.data() == a + 3);
-}
-}
-
-{ // check that ranges::dangling is returned
- [[maybe_unused]] std::same_as<std::ranges::dangling> auto ret = std::ranges::find_last(std::array{1, 2}, 3);
-}
-
-{ // check that an iterator is returned with a borrowing range
- int a[] = {1, 1, 2, 3, 4};
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(std::views::all(a), 1);
- assert(ret.data() == a + 1);
- assert(*(ret.data()) == 1);
-}
-
-{// check that std::invoke is used
-
- {struct S{int i;
-}
-;
-
-S a[] = {S{1}, S{3}, S{2}};
-std::same_as<std::ranges::borrowed_subrange_t<S (&)[3]>> auto ret = std::ranges::find_last(a, 4, &S::i);
-assert(ret.data() == a + 3);
-}
-
-{
- struct S {
- int i;
- };
+ { // check that projections are used properly and called with the reference to the element the iterator is pointing to
+ {
+ int a[] = {1, 2, 3, 4};
+ std::same_as<std::ranges::subrange<int*>> auto ret =
+ std::ranges::find_last(a, a + 4, a + 3, [](int& i) { return &i; });
+ assert(ret.data() == a + 3);
+ }
+
+ {
+ int a[] = {1, 2, 3, 4};
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a, a + 3, [](int& i) { return &i; });
+ assert(ret.data() == a + 3);
+ }
+ }
- S a[] = {S{1}, S{3}, S{2}};
- std::same_as<std::ranges::subrange<S*>> auto ret = std::ranges::find_last(a, a + 3, 4, &S::i);
- assert(ret.data() == a + 3);
-}
-}
+ { // check that the end element is returned
+
+ {
+ struct S {
+ int comp;
+ int other;
+ };
+
+ S a[] = {{0, 0}, {0, 2}, {0, 1}};
+ std::same_as<std::ranges::borrowed_subrange_t<S(&)[3]>> auto ret = std::ranges::find_last(a, 0, &S::comp);
+ assert(ret.data() == a + 2);
+ assert(ret.data()->comp == 0);
+ assert(ret.data()->other == 1);
+ }
+
+ {
+ struct S {
+ int comp;
+ int other;
+ };
+
+ S a[] = {{0, 0}, {0, 2}, {0, 1}};
+ std::same_as<std::ranges::subrange<S*>> auto ret = std::ranges::find_last(a, a + 3, 0, &S::comp);
+ assert(ret.data() == a + 2);
+ assert(ret.data()->comp == 0);
+ assert(ret.data()->other == 1);
+ }
+ }
-{// count invocations of the projection
-
- {int a[] = {1, 2, 2, 3, 4};
-int projection_count = 0;
-std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a, a + 5, 2, [&](int i) {
- ++projection_count;
- return i;
-});
-assert(ret.data() == a + 2);
-assert(*(ret.data()) == 2);
-assert(projection_count == 3);
-}
+ { // check that end + 1 iterator is returned with no match
-{
- int a[] = {1, 2, 2, 3, 4};
- int projection_count = 0;
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a, 2, [&](int i) {
- ++projection_count;
- return i;
- });
- assert(ret.data() == a + 2);
- assert(*(ret.data()) == 2);
- assert(projection_count == 3);
-}
-}
+ {
+ int a[] = {1, 1, 1};
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a, a + 3, 0);
+ assert(ret.data() == a + 3);
+ }
-{// check comparison order
+ {
+ int a[] = {1, 1, 1};
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a, 0);
+ assert(ret.data() == a + 3);
+ }
+ }
- {OneWayComparable a[] = {OneWayComparable{true}};
-std::same_as<std::ranges::subrange<OneWayComparable*>> auto ret =
- std::ranges::find_last(a, a + 1, OneWayComparable{false});
-assert(ret.data() == a);
-}
+ { // check that ranges::dangling is returned
+ [[maybe_unused]] std::same_as<std::ranges::dangling> auto ret = std::ranges::find_last(std::array{1, 2}, 3);
+ }
-{
- OneWayComparable a[] = {OneWayComparable{true}};
- std::same_as<std::ranges::borrowed_subrange_t<OneWayComparable(&)[1]>> auto ret =
- std::ranges::find_last(a, OneWayComparable{false});
- assert(ret.data() == a);
-}
-}
+ { // check that an iterator is returned with a borrowing range
+ int a[] = {1, 1, 2, 3, 4};
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(std::views::all(a), 1);
+ assert(ret.data() == a + 1);
+ assert(*(ret.data()) == 1);
+ }
-{// check that the return type of `iter::operator*` doesn't change
+ { // check that std::invoke is used
- {NonConstComparableLValue a[] = {NonConstComparableLValue{}};
-std::same_as<std::ranges::subrange<NonConstComparableLValue*>> auto ret =
- std::ranges::find_last(a, a + 1, NonConstComparableLValue{});
-assert(ret.data() == a);
-}
+ {
+ struct S {
+ int i;
+ };
-{
- using It = std::move_iterator<NonConstComparableRValue*>;
- NonConstComparableRValue a[] = {NonConstComparableRValue{}};
- std::same_as<std::ranges::subrange<std::move_iterator<NonConstComparableRValue*>>> auto ret =
- std::ranges::find_last(It(a), It(a + 1), NonConstComparableRValue{});
- assert(ret.begin().base() == a);
-}
+ S a[] = {S{1}, S{3}, S{2}};
+ std::same_as<std::ranges::borrowed_subrange_t<S(&)[3]>> auto ret = std::ranges::find_last(a, 4, &S::i);
+ assert(ret.data() == a + 3);
+ }
-{
- NonConstComparableLValue a[] = {NonConstComparableLValue{}};
- std::same_as<std::ranges::borrowed_subrange_t<NonConstComparableLValue(&)[1]>> auto ret =
- std::ranges::find_last(a, NonConstComparableLValue{});
- assert(ret.data() == a);
-}
+ {
+ struct S {
+ int i;
+ };
-{
- using It = std::move_iterator<NonConstComparableRValue*>;
- NonConstComparableRValue a[] = {NonConstComparableRValue{}};
- auto range = std::ranges::subrange(It(a), It(a + 1));
- std::same_as<std::ranges::borrowed_subrange_t<std::ranges::subrange<std::move_iterator<NonConstComparableRValue*>,
- std::move_iterator<NonConstComparableRValue*>,
- std::ranges::subrange_kind::sized>&>> auto ret =
- std::ranges::find_last(range, NonConstComparableRValue{});
- assert(ret.begin().base() == a);
-}
-}
+ S a[] = {S{1}, S{3}, S{2}};
+ std::same_as<std::ranges::subrange<S*>> auto ret = std::ranges::find_last(a, a + 3, 4, &S::i);
+ assert(ret.data() == a + 3);
+ }
+ }
-{// check that an empty range works
- {std::array<int, 0> a = {};
-int search_value = 1;
-std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a.begin(), a.end(), search_value);
-assert(ret.data() == a.end());
-}
+ { // count invocations of the projection
+
+ {
+ int a[] = {1, 2, 2, 3, 4};
+ int projection_count = 0;
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a, a + 5, 2, [&](int i) {
+ ++projection_count;
+ return i;
+ });
+ assert(ret.data() == a + 2);
+ assert(*(ret.data()) == 2);
+ assert(projection_count == 3);
+ }
+
+ {
+ int a[] = {1, 2, 2, 3, 4};
+ int projection_count = 0;
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a, 2, [&](int i) {
+ ++projection_count;
+ return i;
+ });
+ assert(ret.data() == a + 2);
+ assert(*(ret.data()) == 2);
+ assert(projection_count == 3);
+ }
+ }
-{
- std::array<int, 0> a = {};
- int search_value = 1;
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a, search_value);
- assert(ret.data() == a.end());
-}
-}
+ { // check comparison order
+
+ {
+ OneWayComparable a[] = {OneWayComparable{true}};
+ std::same_as<std::ranges::subrange<OneWayComparable*>> auto ret =
+ std::ranges::find_last(a, a + 1, OneWayComparable{false});
+ assert(ret.data() == a);
+ }
+
+ {
+ OneWayComparable a[] = {OneWayComparable{true}};
+ std::same_as<std::ranges::borrowed_subrange_t<OneWayComparable(&)[1]>> auto ret =
+ std::ranges::find_last(a, OneWayComparable{false});
+ assert(ret.data() == a);
+ }
+ }
-{ // check that the implicit conversion to bool works
+ { // check that the return type of `iter::operator*` doesn't change
+
+ {
+ NonConstComparableLValue a[] = {NonConstComparableLValue{}};
+ std::same_as<std::ranges::subrange<NonConstComparableLValue*>> auto ret =
+ std::ranges::find_last(a, a + 1, NonConstComparableLValue{});
+ assert(ret.data() == a);
+ }
+
+ {
+ using It = std::move_iterator<NonConstComparableRValue*>;
+ NonConstComparableRValue a[] = {NonConstComparableRValue{}};
+ std::same_as<std::ranges::subrange<std::move_iterator<NonConstComparableRValue*>>> auto ret =
+ std::ranges::find_last(It(a), It(a + 1), NonConstComparableRValue{});
+ assert(ret.begin().base() == a);
+ }
+
+ {
+ NonConstComparableLValue a[] = {NonConstComparableLValue{}};
+ std::same_as<std::ranges::borrowed_subrange_t<NonConstComparableLValue(&)[1]>> auto ret =
+ std::ranges::find_last(a, NonConstComparableLValue{});
+ assert(ret.data() == a);
+ }
+
+ {
+ using It = std::move_iterator<NonConstComparableRValue*>;
+ NonConstComparableRValue a[] = {NonConstComparableRValue{}};
+ auto range = std::ranges::subrange(It(a), It(a + 1));
+ std::same_as<std::ranges::borrowed_subrange_t<std::ranges::subrange<std::move_iterator<NonConstComparableRValue*>,
+ std::move_iterator<NonConstComparableRValue*>,
+ std::ranges::subrange_kind::sized>&>> auto
+ ret = std::ranges::find_last(range, NonConstComparableRValue{});
+ assert(ret.begin().base() == a);
+ }
+ }
- {
- StrictComparable<int> a[] = {1, 2, 2, 3, 4};
- std::same_as<std::ranges::subrange<StrictComparable<int>*>> auto ret =
- std::ranges::find_last(a, a + 4, StrictComparable<int>{2});
- assert(ret.data() == a + 2);
+ { // check that an empty range works
+ {
+ std::array<int, 0> a = {};
+ int search_value = 1;
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a.begin(), a.end(), search_value);
+ assert(ret.data() == a.end());
+ }
+
+ {
+ std::array<int, 0> a = {};
+ int search_value = 1;
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last(a, search_value);
+ assert(ret.data() == a.end());
+ }
}
- {
- StrictComparable<int> a[] = {1, 2, 2, 3, 4};
- std::same_as<std::ranges::borrowed_subrange_t<StrictComparable<int>(&)[5]>> auto ret =
- std::ranges::find_last(a, StrictComparable<int>{2});
- assert(ret.data() == a + 2);
+ { // check that the implicit conversion to bool works
+
+ {
+ StrictComparable<int> a[] = {1, 2, 2, 3, 4};
+ std::same_as<std::ranges::subrange<StrictComparable<int>*>> auto ret =
+ std::ranges::find_last(a, a + 4, StrictComparable<int>{2});
+ assert(ret.data() == a + 2);
+ }
+
+ {
+ StrictComparable<int> a[] = {1, 2, 2, 3, 4};
+ std::same_as<std::ranges::borrowed_subrange_t<StrictComparable<int>(&)[5]>> auto ret =
+ std::ranges::find_last(a, StrictComparable<int>{2});
+ assert(ret.data() == a + 2);
+ }
}
-}
-return true;
+ return true;
}
int main(int, char**) {
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last_if.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last_if.pass.cpp
index 881b71e7494..c145293f06c 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last_if.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last_if.pass.cpp
@@ -32,9 +32,7 @@ struct Predicate {
};
template <class It, class Sent = It>
-concept HasFindLastIfIt = requires(It it, Sent sent) {
- std::ranges::find_last_if(it, sent, Predicate{});
-};
+concept HasFindLastIfIt = requires(It it, Sent sent) { std::ranges::find_last_if(it, sent, Predicate{}); };
static_assert(HasFindLastIfIt<int*>);
static_assert(!HasFindLastIfIt<InputIteratorNotDerivedFrom>);
static_assert(!HasFindLastIfIt<InputIteratorNotIndirectlyReadable>);
@@ -46,18 +44,14 @@ static_assert(!HasFindLastIfIt<int*, int>);
static_assert(!HasFindLastIfIt<int, int*>);
template <class Pred>
-concept HasFindLastIfPred = requires(int* it, Pred pred) {
- std::ranges::find_last_if(it, it, pred);
-};
+concept HasFindLastIfPred = requires(int* it, Pred pred) { std::ranges::find_last_if(it, it, pred); };
static_assert(HasFindLastIfPred<IndirectUnaryPredicate>);
static_assert(!HasFindLastIfPred<IndirectUnaryPredicateNotCopyConstructible>);
static_assert(!HasFindLastIfPred<IndirectUnaryPredicateNotPredicate>);
template <class R>
-concept HasFindLastIfR = requires(R r) {
- std::ranges::find_last_if(r, Predicate{});
-};
+concept HasFindLastIfR = requires(R r) { std::ranges::find_last_if(r, Predicate{}); };
static_assert(HasFindLastIfR<std::array<int, 0>>);
static_assert(!HasFindLastIfR<int>);
@@ -69,125 +63,132 @@ static_assert(!HasFindLastIfR<InputRangeNotSentinelEqualityComparableWith>);
template <class It, class Sent = It>
constexpr void test_iterators() {
- {// Test with an empty range
-
- {int a[] = {};
- std::same_as<std::ranges::subrange<It>> auto ret =
- std::ranges::find_last_if(It(a), Sent(It(a)), [](int x) { return x == 4; });
- assert(ret.empty());
-}
-
-{
- int a[] = {};
- auto range = std::ranges::subrange(It(a), Sent(It(a)));
- std::same_as<std::ranges::subrange<It>> auto ret = std::ranges::find_last_if(range, [](int x) { return x == 4; });
- assert(ret.empty());
-}
-}
-
-{// Test with a single element range
-
- {int a[] = {4};
-std::same_as<std::ranges::subrange<It>> auto ret =
- std::ranges::find_last_if(It(a), Sent(It(a + 1)), [](int x) { return x == 4; });
-assert(base(ret.begin()) == a);
-assert(*ret.begin() == 4);
-}
-
-{
- int a[] = {4};
- std::same_as<std::ranges::borrowed_subrange_t<int(&)[1]>> auto ret =
- std::ranges::find_last_if(a, [](int x) { return x == 4; });
- assert(base(ret.begin()) == a);
- assert(*ret.begin() == 4);
-}
-}
-
-{// Test when no element satisfies the predicate
-
- {int a[] = {1, 2, 3, 4};
-std::same_as<std::ranges::subrange<It>> auto ret =
- std::ranges::find_last_if(It(a), Sent(It(a + 4)), [](int x) { return x == 5; });
-assert(ret.empty());
-}
-
-{
- int a[] = {1, 2, 3, 4};
- std::same_as<std::ranges::borrowed_subrange_t<int(&)[4]>> auto ret =
- std::ranges::find_last_if(a, [](int x) { return x == 5; });
- assert(ret.empty());
-}
-}
-
-{// Test when all elements satisfy the predicate
-
- {int a[] = {4, 4, 4, 4};
-std::same_as<std::ranges::subrange<It>> auto ret =
- std::ranges::find_last_if(It(a), Sent(It(a + 4)), [](int x) { return x == 4; });
-assert(base(ret.begin()) == a + 3);
-assert(*ret.begin() == 4);
-}
-
-{
- int a[] = {4, 4, 4, 4};
- std::same_as<std::ranges::borrowed_subrange_t<int(&)[4]>> auto ret =
- std::ranges::find_last_if(a, [](int x) { return x == 4; });
- assert(base(ret.begin()) == a + 3);
- assert(*ret.begin() == 4);
-}
-}
-
-{// Test when the element being searched is the first one
+ { // Test with an empty range
+
+ {
+ int a[] = {};
+ std::same_as<std::ranges::subrange<It>> auto ret =
+ std::ranges::find_last_if(It(a), Sent(It(a)), [](int x) { return x == 4; });
+ assert(ret.empty());
+ }
+
+ {
+ int a[] = {};
+ auto range = std::ranges::subrange(It(a), Sent(It(a)));
+ std::same_as<std::ranges::subrange<It>> auto ret = std::ranges::find_last_if(range, [](int x) { return x == 4; });
+ assert(ret.empty());
+ }
+ }
- {int a[] = {4, 1, 2, 3};
-std::same_as<std::ranges::subrange<It>> auto ret =
- std::ranges::find_last_if(It(a), Sent(It(a + 4)), [](int x) { return x == 4; });
-assert(base(ret.begin()) == a);
-assert(*ret.begin() == 4);
-}
+ { // Test with a single element range
+
+ {
+ int a[] = {4};
+ std::same_as<std::ranges::subrange<It>> auto ret =
+ std::ranges::find_last_if(It(a), Sent(It(a + 1)), [](int x) { return x == 4; });
+ assert(base(ret.begin()) == a);
+ assert(*ret.begin() == 4);
+ }
+
+ {
+ int a[] = {4};
+ std::same_as<std::ranges::borrowed_subrange_t<int(&)[1]>> auto ret =
+ std::ranges::find_last_if(a, [](int x) { return x == 4; });
+ assert(base(ret.begin()) == a);
+ assert(*ret.begin() == 4);
+ }
+ }
-{
- int a[] = {4, 1, 2, 3};
- std::same_as<std::ranges::borrowed_subrange_t<int(&)[4]>> auto ret =
- std::ranges::find_last_if(a, [](int x) { return x == 4; });
- assert(base(ret.begin()) == a);
- assert(*ret.begin() == 4);
-}
-}
+ { // Test when no element satisfies the predicate
+
+ {
+ int a[] = {1, 2, 3, 4};
+ std::same_as<std::ranges::subrange<It>> auto ret =
+ std::ranges::find_last_if(It(a), Sent(It(a + 4)), [](int x) { return x == 5; });
+ assert(ret.empty());
+ }
+
+ {
+ int a[] = {1, 2, 3, 4};
+ std::same_as<std::ranges::borrowed_subrange_t<int(&)[4]>> auto ret =
+ std::ranges::find_last_if(a, [](int x) { return x == 5; });
+ assert(ret.empty());
+ }
+ }
-{// Test when the element being searched is the last one
+ { // Test when all elements satisfy the predicate
+
+ {
+ int a[] = {4, 4, 4, 4};
+ std::same_as<std::ranges::subrange<It>> auto ret =
+ std::ranges::find_last_if(It(a), Sent(It(a + 4)), [](int x) { return x == 4; });
+ assert(base(ret.begin()) == a + 3);
+ assert(*ret.begin() == 4);
+ }
+
+ {
+ int a[] = {4, 4, 4, 4};
+ std::same_as<std::ranges::borrowed_subrange_t<int(&)[4]>> auto ret =
+ std::ranges::find_last_if(a, [](int x) { return x == 4; });
+ assert(base(ret.begin()) == a + 3);
+ assert(*ret.begin() == 4);
+ }
+ }
- {int a[] = {1, 2, 3, 4};
-std::same_as<std::ranges::subrange<It>> auto ret =
- std::ranges::find_last_if(It(a), Sent(It(a + 4)), [](int x) { return x == 4; });
-assert(base(ret.begin()) == a + 3);
-assert(*ret.begin() == 4);
-}
+ { // Test when the element being searched is the first one
+
+ {
+ int a[] = {4, 1, 2, 3};
+ std::same_as<std::ranges::subrange<It>> auto ret =
+ std::ranges::find_last_if(It(a), Sent(It(a + 4)), [](int x) { return x == 4; });
+ assert(base(ret.begin()) == a);
+ assert(*ret.begin() == 4);
+ }
+
+ {
+ int a[] = {4, 1, 2, 3};
+ std::same_as<std::ranges::borrowed_subrange_t<int(&)[4]>> auto ret =
+ std::ranges::find_last_if(a, [](int x) { return x == 4; });
+ assert(base(ret.begin()) == a);
+ assert(*ret.begin() == 4);
+ }
+ }
-{
- int a[] = {1, 2, 3, 4};
- std::same_as<std::ranges::borrowed_subrange_t<int(&)[4]>> auto ret =
- std::ranges::find_last_if(a, [](int x) { return x == 4; });
- assert(base(ret.begin()) == a + 3);
- assert(*ret.begin() == 4);
-}
-}
+ { // Test when the element being searched is the last one
+
+ {
+ int a[] = {1, 2, 3, 4};
+ std::same_as<std::ranges::subrange<It>> auto ret =
+ std::ranges::find_last_if(It(a), Sent(It(a + 4)), [](int x) { return x == 4; });
+ assert(base(ret.begin()) == a + 3);
+ assert(*ret.begin() == 4);
+ }
+
+ {
+ int a[] = {1, 2, 3, 4};
+ std::same_as<std::ranges::borrowed_subrange_t<int(&)[4]>> auto ret =
+ std::ranges::find_last_if(a, [](int x) { return x == 4; });
+ assert(base(ret.begin()) == a + 3);
+ assert(*ret.begin() == 4);
+ }
+ }
-{ // check that past-the-end iterator is returned with no match
+ { // check that past-the-end iterator is returned with no match
- {
- int a[] = {1, 1, 1};
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if(a, a + 3, [](int) { return false; });
- assert(ret.data() == a + 3);
- }
+ {
+ int a[] = {1, 1, 1};
+ std::same_as<std::ranges::subrange<int*>> auto ret =
+ std::ranges::find_last_if(a, a + 3, [](int) { return false; });
+ assert(ret.data() == a + 3);
+ }
- {
- int a[] = {1, 1, 1};
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if(a, [](int) { return false; });
- assert(ret.data() == a + 3);
+ {
+ int a[] = {1, 1, 1};
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if(a, [](int) { return false; });
+ assert(ret.data() == a + 3);
+ }
}
}
-}
struct NonConstComparableValue {
friend constexpr bool operator==(const NonConstComparableValue&, const NonConstComparableValue&) { return false; }
@@ -205,165 +206,170 @@ constexpr bool test() {
test_iterators<random_access_iterator<int*>>();
test_iterators<contiguous_iterator<int*>>();
- {// check that projections are used properly and called with the reference to the element the iterator is pointing to
-
- {int a[] = {1, 2, 3, 4};
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if(
- a, a + 4, [&](int* i) { return i == a + 3; }, [](int& i) { return &i; });
- assert(ret.data() == a + 3);
-}
-
-{
- int a[] = {1, 2, 3, 4};
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if(
- a, [&](int* i) { return i == a + 3; }, [](int& i) { return &i; });
- assert(ret.data() == a + 3);
-}
-}
-
-{// check that the last element is returned
-
- {struct S{int comp;
-int other;
-}
-;
-
-S a[] = {{0, 0}, {0, 2}, {0, 1}};
-std::same_as<std::ranges::borrowed_subrange_t<S (&)[3]>> auto ret = std::ranges::find_last_if(
- a, [](int i) { return i == 0; }, &S::comp);
-assert(ret.data() == a + 2);
-assert(ret.data()->comp == 0);
-assert(ret.data()->other == 1);
-}
-
-{
- struct S {
- int comp;
- int other;
- };
-
- S a[] = {{0, 0}, {0, 2}, {0, 1}};
- std::same_as<std::ranges::subrange<S*>> auto ret = std::ranges::find_last_if(
- a, a + 3, [](int i) { return i == 0; }, &S::comp);
- assert(ret.data() == a + 2);
- assert(ret.data()->comp == 0);
- assert(ret.data()->other == 1);
-}
-}
-
-{ // check that ranges::dangling is returned
- [[maybe_unused]] std::same_as<std::ranges::dangling> auto ret =
- std::ranges::find_last_if(std::array{1, 2}, [](int) { return false; });
-}
-
-{ // check that an iterator is returned with a borrowing range
- int a[] = {1, 1, 2, 3, 4};
- std::same_as<std::ranges::subrange<int*>> auto ret =
- std::ranges::find_last_if(std::views::all(a), [](int) { return true; });
- assert(ret.data() == a + 4);
-}
-
-{// check that std::invoke is used
-
- {struct S{int i;
-}
-;
-
-S a[] = {S{1}, S{3}, S{2}};
-std::same_as<std::ranges::borrowed_subrange_t<S (&)[3]>> auto ret = std::ranges::find_last_if(
- a, [](int) { return false; }, &S::i);
-assert(ret.data() == a + 3);
-}
-
-{
- struct S {
- int i;
- };
-
- S a[] = {S{1}, S{3}, S{2}};
- std::same_as<std::ranges::subrange<S*>> auto ret = std::ranges::find_last_if(
- a, a + 3, [](int) { return false; }, &S::i);
- assert(ret.data() == a + 3);
-}
-}
-
-{// count projection and predicate invocation count
-
- {int a[] = {1, 2, 2, 3, 4};
-int predicate_count = 0;
-int projection_count = 0;
-std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if(
- a,
- a + 5,
- [&](int i) {
- ++predicate_count;
- return i == 2;
- },
- [&](int i) {
- ++projection_count;
- return i;
- });
-assert(ret.data() == a + 2);
-assert(*(ret.data()) == 2);
-assert(predicate_count == 3);
-assert(projection_count == 3);
-}
+ { // check that projections are used properly and called with the reference to the element the iterator is pointing to
+
+ {
+ int a[] = {1, 2, 3, 4};
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if(
+ a, a + 4, [&](int* i) { return i == a + 3; }, [](int& i) { return &i; });
+ assert(ret.data() == a + 3);
+ }
+
+ {
+ int a[] = {1, 2, 3, 4};
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if(
+ a, [&](int* i) { return i == a + 3; }, [](int& i) { return &i; });
+ assert(ret.data() == a + 3);
+ }
+ }
-{
- int a[] = {1, 2, 2, 3, 4};
- int predicate_count = 0;
- int projection_count = 0;
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if(
- a,
- [&](int i) {
- ++predicate_count;
- return i == 2;
- },
- [&](int i) {
- ++projection_count;
- return i;
- });
- assert(ret.data() == a + 2);
- assert(*(ret.data()) == 2);
- assert(predicate_count == 3);
- assert(projection_count == 3);
-}
-}
+ { // check that the last element is returned
+
+ {
+ struct S {
+ int comp;
+ int other;
+ };
+
+ S a[] = {{0, 0}, {0, 2}, {0, 1}};
+ std::same_as<std::ranges::borrowed_subrange_t<S(&)[3]>> auto ret = std::ranges::find_last_if(
+ a, [](int i) { return i == 0; }, &S::comp);
+ assert(ret.data() == a + 2);
+ assert(ret.data()->comp == 0);
+ assert(ret.data()->other == 1);
+ }
+
+ {
+ struct S {
+ int comp;
+ int other;
+ };
+
+ S a[] = {{0, 0}, {0, 2}, {0, 1}};
+ std::same_as<std::ranges::subrange<S*>> auto ret = std::ranges::find_last_if(
+ a, a + 3, [](int i) { return i == 0; }, &S::comp);
+ assert(ret.data() == a + 2);
+ assert(ret.data()->comp == 0);
+ assert(ret.data()->other == 1);
+ }
+ }
-{// check that the return type of `iter::operator*` doesn't change
+ { // check that ranges::dangling is returned
+ [[maybe_unused]] std::same_as<std::ranges::dangling> auto ret =
+ std::ranges::find_last_if(std::array{1, 2}, [](int) { return false; });
+ }
- {NonConstComparableValue a[] = {NonConstComparableValue{}};
-std::same_as<std::ranges::subrange<NonConstComparableValue*>> auto ret =
- std::ranges::find_last_if(a, a + 1, [](auto&& e) { return e == NonConstComparableValue{}; });
-assert(ret.data() == a);
-}
+ { // check that an iterator is returned with a borrowing range
+ int a[] = {1, 1, 2, 3, 4};
+ std::same_as<std::ranges::subrange<int*>> auto ret =
+ std::ranges::find_last_if(std::views::all(a), [](int) { return true; });
+ assert(ret.data() == a + 4);
+ }
-{
- NonConstComparableValue a[] = {NonConstComparableValue{}};
- std::same_as<std::ranges::borrowed_subrange_t<NonConstComparableValue(&)[1]>> auto ret =
- std::ranges::find_last_if(a, [](auto&& e) { return e == NonConstComparableValue{}; });
- assert(ret.data() == a);
-}
-}
+ { // check that std::invoke is used
+
+ {
+ struct S {
+ int i;
+ };
+
+ S a[] = {S{1}, S{3}, S{2}};
+ std::same_as<std::ranges::borrowed_subrange_t<S(&)[3]>> auto ret = std::ranges::find_last_if(
+ a, [](int) { return false; }, &S::i);
+ assert(ret.data() == a + 3);
+ }
+
+ {
+ struct S {
+ int i;
+ };
+
+ S a[] = {S{1}, S{3}, S{2}};
+ std::same_as<std::ranges::subrange<S*>> auto ret = std::ranges::find_last_if(
+ a, a + 3, [](int) { return false; }, &S::i);
+ assert(ret.data() == a + 3);
+ }
+ }
-{ // check that the implicit conversion to bool works
+ { // count projection and predicate invocation count
+
+ {
+ int a[] = {1, 2, 2, 3, 4};
+ int predicate_count = 0;
+ int projection_count = 0;
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if(
+ a,
+ a + 5,
+ [&](int i) {
+ ++predicate_count;
+ return i == 2;
+ },
+ [&](int i) {
+ ++projection_count;
+ return i;
+ });
+ assert(ret.data() == a + 2);
+ assert(*(ret.data()) == 2);
+ assert(predicate_count == 3);
+ assert(projection_count == 3);
+ }
+
+ {
+ int a[] = {1, 2, 2, 3, 4};
+ int predicate_count = 0;
+ int projection_count = 0;
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if(
+ a,
+ [&](int i) {
+ ++predicate_count;
+ return i == 2;
+ },
+ [&](int i) {
+ ++projection_count;
+ return i;
+ });
+ assert(ret.data() == a + 2);
+ assert(*(ret.data()) == 2);
+ assert(predicate_count == 3);
+ assert(projection_count == 3);
+ }
+ }
- {
- int a[] = {1, 2, 3, 3, 4};
- std::same_as<std::ranges::subrange<int*>> auto ret =
- std::ranges::find_last_if(a, a + 4, [](const int& i) { return BooleanTestable{i == 3}; });
- assert(ret.data() == a + 3);
+ { // check that the return type of `iter::operator*` doesn't change
+
+ {
+ NonConstComparableValue a[] = {NonConstComparableValue{}};
+ std::same_as<std::ranges::subrange<NonConstComparableValue*>> auto ret =
+ std::ranges::find_last_if(a, a + 1, [](auto&& e) { return e == NonConstComparableValue{}; });
+ assert(ret.data() == a);
+ }
+
+ {
+ NonConstComparableValue a[] = {NonConstComparableValue{}};
+ std::same_as<std::ranges::borrowed_subrange_t<NonConstComparableValue(&)[1]>> auto ret =
+ std::ranges::find_last_if(a, [](auto&& e) { return e == NonConstComparableValue{}; });
+ assert(ret.data() == a);
+ }
}
- {
- int a[] = {1, 2, 3, 3, 4};
- std::same_as<std::ranges::subrange<int*>> auto ret =
- std::ranges::find_last_if(a, [](const int& b) { return BooleanTestable{b == 3}; });
- assert(ret.data() == a + 3);
+ { // check that the implicit conversion to bool works
+
+ {
+ int a[] = {1, 2, 3, 3, 4};
+ std::same_as<std::ranges::subrange<int*>> auto ret =
+ std::ranges::find_last_if(a, a + 4, [](const int& i) { return BooleanTestable{i == 3}; });
+ assert(ret.data() == a + 3);
+ }
+
+ {
+ int a[] = {1, 2, 3, 3, 4};
+ std::same_as<std::ranges::subrange<int*>> auto ret =
+ std::ranges::find_last_if(a, [](const int& b) { return BooleanTestable{b == 3}; });
+ assert(ret.data() == a + 3);
+ }
}
-}
-return true;
+ return true;
}
int main(int, char**) {
diff --git a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last_if_not.pass.cpp b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last_if_not.pass.cpp
index 76d3e7148a8..52a4266e9b9 100644
--- a/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last_if_not.pass.cpp
+++ b/libcxx/test/std/algorithms/alg.nonmodifying/alg.find/ranges.find_last_if_not.pass.cpp
@@ -32,9 +32,7 @@ struct Predicate {
};
template <class It, class Sent = It>
-concept HasFindLastIfNotIt = requires(It it, Sent sent) {
- std::ranges::find_last_if_not(it, sent, Predicate{});
-};
+concept HasFindLastIfNotIt = requires(It it, Sent sent) { std::ranges::find_last_if_not(it, sent, Predicate{}); };
static_assert(HasFindLastIfNotIt<int*>);
static_assert(!HasFindLastIfNotIt<InputIteratorNotDerivedFrom>);
static_assert(!HasFindLastIfNotIt<InputIteratorNotIndirectlyReadable>);
@@ -46,18 +44,14 @@ static_assert(!HasFindLastIfNotIt<int*, int>);
static_assert(!HasFindLastIfNotIt<int, int*>);
template <class Pred>
-concept HasFindLastIfNotPred = requires(int* it, Pred pred) {
- std::ranges::find_last_if_not(it, it, pred);
-};
+concept HasFindLastIfNotPred = requires(int* it, Pred pred) { std::ranges::find_last_if_not(it, it, pred); };
static_assert(HasFindLastIfNotPred<IndirectUnaryPredicate>);
static_assert(!HasFindLastIfNotPred<IndirectUnaryPredicateNotCopyConstructible>);
static_assert(!HasFindLastIfNotPred<IndirectUnaryPredicateNotPredicate>);
template <class R>
-concept HasFindLastIfNotR = requires(R r) {
- std::ranges::find_last_if_not(r, Predicate{});
-};
+concept HasFindLastIfNotR = requires(R r) { std::ranges::find_last_if_not(r, Predicate{}); };
static_assert(HasFindLastIfNotR<std::array<int, 0>>);
static_assert(!HasFindLastIfNotR<int>);
static_assert(!HasFindLastIfNotR<InputRangeNotDerivedFrom>);
@@ -68,77 +62,81 @@ static_assert(!HasFindLastIfNotR<InputRangeNotSentinelEqualityComparableWith>);
template <class It, class Sent = It>
constexpr void test_iterators() {
- {// Test with an empty range
-
- {int a[] = {};
- std::same_as<std::ranges::subrange<It>> auto ret =
- std::ranges::find_last_if_not(It(a), Sent(It(a)), [](int) { return false; });
- assert(ret.empty());
-}
-
-{
- int a[] = {};
- auto range = std::ranges::subrange(It(a), Sent(It(a)));
- std::same_as<std::ranges::subrange<It>> auto ret = std::ranges::find_last_if_not(range, [](int) { return false; });
- assert(ret.empty());
-}
-}
-
-{// Test with a single element range
-
- {int a[] = {1};
-std::same_as<std::ranges::subrange<It>> auto ret =
- std::ranges::find_last_if_not(It(a), Sent(It(a + 1)), [](int) { return false; });
-assert(base(ret.begin()) == a);
-assert(*ret.begin() == 1);
-}
-
-{
- int a[] = {1};
- std::same_as<std::ranges::borrowed_subrange_t<int(&)[1]>> auto ret =
- std::ranges::find_last_if_not(a, [](int) { return false; });
- assert(base(ret.begin()) == a);
- assert(*ret.begin() == 1);
-}
-}
-
-{// Test with a range where each element satisfies the predicate
-
- {int a[] = {1, 2, 3, 4, 5};
-std::same_as<std::ranges::subrange<It>> auto ret =
- std::ranges::find_last_if_not(It(a), Sent(It(a + 5)), [](int) { return false; });
-assert(base(ret.begin()) == a + 4);
-assert(*ret.begin() == 5);
-}
-
-{
- int a[] = {1, 2, 3, 4, 5};
- std::same_as<std::ranges::borrowed_subrange_t<int(&)[5]>> auto ret =
- std::ranges::find_last_if_not(a, [](int) { return false; });
- assert(base(ret.begin()) == a + 4);
- assert(*ret.begin() == 5);
-}
-}
+ { // Test with an empty range
+
+ {
+ int a[] = {};
+ std::same_as<std::ranges::subrange<It>> auto ret =
+ std::ranges::find_last_if_not(It(a), Sent(It(a)), [](int) { return false; });
+ assert(ret.empty());
+ }
+
+ {
+ int a[] = {};
+ auto range = std::ranges::subrange(It(a), Sent(It(a)));
+ std::same_as<std::ranges::subrange<It>> auto ret =
+ std::ranges::find_last_if_not(range, [](int) { return false; });
+ assert(ret.empty());
+ }
+ }
-{ // Test with a range where no element satisfies the predicate
+ { // Test with a single element range
+
+ {
+ int a[] = {1};
+ std::same_as<std::ranges::subrange<It>> auto ret =
+ std::ranges::find_last_if_not(It(a), Sent(It(a + 1)), [](int) { return false; });
+ assert(base(ret.begin()) == a);
+ assert(*ret.begin() == 1);
+ }
+
+ {
+ int a[] = {1};
+ std::same_as<std::ranges::borrowed_subrange_t<int(&)[1]>> auto ret =
+ std::ranges::find_last_if_not(a, [](int) { return false; });
+ assert(base(ret.begin()) == a);
+ assert(*ret.begin() == 1);
+ }
+ }
- {
- int a[] = {1, 2, 3, 4, 5};
- std::same_as<std::ranges::subrange<It>> auto ret =
- std::ranges::find_last_if_not(It(a), Sent(It(a + 5)), [](int) { return true; });
- //assert(base(ret.begin()) == a + 5);
- assert(ret.empty());
+ { // Test with a range where each element satisfies the predicate
+
+ {
+ int a[] = {1, 2, 3, 4, 5};
+ std::same_as<std::ranges::subrange<It>> auto ret =
+ std::ranges::find_last_if_not(It(a), Sent(It(a + 5)), [](int) { return false; });
+ assert(base(ret.begin()) == a + 4);
+ assert(*ret.begin() == 5);
+ }
+
+ {
+ int a[] = {1, 2, 3, 4, 5};
+ std::same_as<std::ranges::borrowed_subrange_t<int(&)[5]>> auto ret =
+ std::ranges::find_last_if_not(a, [](int) { return false; });
+ assert(base(ret.begin()) == a + 4);
+ assert(*ret.begin() == 5);
+ }
}
- {
- int a[] = {1, 2, 3, 4, 5};
- std::same_as<std::ranges::borrowed_subrange_t<int(&)[5]>> auto ret =
- std::ranges::find_last_if_not(a, [](int) { return true; });
- //assert(base(ret.begin()) == a + 5);
- assert(ret.empty());
+ { // Test with a range where no element satisfies the predicate
+
+ {
+ int a[] = {1, 2, 3, 4, 5};
+ std::same_as<std::ranges::subrange<It>> auto ret =
+ std::ranges::find_last_if_not(It(a), Sent(It(a + 5)), [](int) { return true; });
+ //assert(base(ret.begin()) == a + 5);
+ assert(ret.empty());
+ }
+
+ {
+ int a[] = {1, 2, 3, 4, 5};
+ std::same_as<std::ranges::borrowed_subrange_t<int(&)[5]>> auto ret =
+ std::ranges::find_last_if_not(a, [](int) { return true; });
+ //assert(base(ret.begin()) == a + 5);
+ assert(ret.empty());
+ }
}
}
-}
struct NonConstComparableValue {
friend constexpr bool operator==(const NonConstComparableValue&, const NonConstComparableValue&) { return false; }
@@ -156,178 +154,185 @@ constexpr bool test() {
test_iterators<random_access_iterator<int*>>();
test_iterators<contiguous_iterator<int*>>();
- {// check that projections are used properly and called with the reference to the element the iterator is pointing to
-
- {int a[] = {1, 2, 3, 4, 5};
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if_not(
- a, a + 5, [&](int* i) { return i != a + 3; }, [](int& i) { return &i; });
- assert(ret.data() == a + 3);
-}
-
-{
- int a[] = {1, 2, 3, 4, 5};
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if_not(
- a, [&](int* i) { return i != a + 3; }, [](int& i) { return &i; });
- assert(ret.data() == a + 3);
-}
-}
-
-{// check that the last element is returned
-
- {struct S{int comp;
-int other;
-}
-;
-
-S a[] = {{0, 0}, {0, 2}, {0, 1}};
-std::same_as<std::ranges::borrowed_subrange_t<S (&)[3]>> auto ret = std::ranges::find_last_if_not(
- a, [](int i) { return i != 0; }, &S::comp);
-assert(ret.data() == a + 2);
-assert(ret.data()->comp == 0);
-assert(ret.data()->other == 1);
-}
-
-{
- struct S {
- int comp;
- int other;
- };
-
- S a[] = {{0, 0}, {0, 2}, {0, 1}};
- std::same_as<std::ranges::subrange<S*>> auto ret = std::ranges::find_last_if_not(
- a, a + 3, [](int i) { return i != 0; }, &S::comp);
- assert(ret.data() == a + 2);
- assert(ret.data()->comp == 0);
- assert(ret.data()->other == 1);
-}
-}
-
-{// check that end iterator is returned with no match
-
- {int a[] = {1, 1, 1};
-std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if_not(a, a + 3, [](int) { return false; });
-assert(ret.data() == a + 2);
-}
-
-{
- int a[] = {1, 1, 1};
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if_not(a, [](int) { return false; });
- assert(ret.data() == a + 2);
-}
-}
-
-{ // check that ranges::dangling is returned
- [[maybe_unused]] std::same_as<std::ranges::dangling> auto ret =
- std::ranges::find_last_if_not(std::array{1, 2}, [](int) { return true; });
-}
-
-{ // check that an iterator is returned with a borrowing range
- int a[] = {1, 2, 3, 4};
- std::same_as<std::ranges::subrange<int*>> auto ret =
- std::ranges::find_last_if_not(std::views::all(a), [](int) { return false; });
- assert(ret.data() == a + 3);
- assert(*ret.data() == 4);
-}
-
-{// check that std::invoke is used
-
- {struct S{int i;
-}
-;
+ { // check that projections are used properly and called with the reference to the element the iterator is pointing to
+
+ {
+ int a[] = {1, 2, 3, 4, 5};
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if_not(
+ a, a + 5, [&](int* i) { return i != a + 3; }, [](int& i) { return &i; });
+ assert(ret.data() == a + 3);
+ }
+
+ {
+ int a[] = {1, 2, 3, 4, 5};
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if_not(
+ a, [&](int* i) { return i != a + 3; }, [](int& i) { return &i; });
+ assert(ret.data() == a + 3);
+ }
+ }
-S a[] = {S{1}, S{3}, S{2}};
-std::same_as<std::ranges::borrowed_subrange_t<S (&)[3]>> auto ret = std::ranges::find_last_if_not(
- a, [](int) { return true; }, &S::i);
-assert(ret.data() == a + 3);
-}
+ { // check that the last element is returned
+
+ {
+ struct S {
+ int comp;
+ int other;
+ };
+
+ S a[] = {{0, 0}, {0, 2}, {0, 1}};
+ std::same_as<std::ranges::borrowed_subrange_t<S(&)[3]>> auto ret = std::ranges::find_last_if_not(
+ a, [](int i) { return i != 0; }, &S::comp);
+ assert(ret.data() == a + 2);
+ assert(ret.data()->comp == 0);
+ assert(ret.data()->other == 1);
+ }
+
+ {
+ struct S {
+ int comp;
+ int other;
+ };
+
+ S a[] = {{0, 0}, {0, 2}, {0, 1}};
+ std::same_as<std::ranges::subrange<S*>> auto ret = std::ranges::find_last_if_not(
+ a, a + 3, [](int i) { return i != 0; }, &S::comp);
+ assert(ret.data() == a + 2);
+ assert(ret.data()->comp == 0);
+ assert(ret.data()->other == 1);
+ }
+ }
-{
- struct S {
- int i;
- };
+ { // check that end iterator is returned with no match
- S a[] = {S{1}, S{3}, S{2}};
- std::same_as<std::ranges::subrange<S*>> auto ret = std::ranges::find_last_if_not(
- a, a + 3, [](int) { return true; }, &S::i);
- assert(ret.data() == a + 3);
-}
-}
+ {
+ int a[] = {1, 1, 1};
+ std::same_as<std::ranges::subrange<int*>> auto ret =
+ std::ranges::find_last_if_not(a, a + 3, [](int) { return false; });
+ assert(ret.data() == a + 2);
+ }
-{// count projection and predicate invocation count
-
- {int a[] = {1, 2, 3, 4};
-int predicate_count = 0;
-int projection_count = 0;
-std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if_not(
- a,
- a + 4,
- [&](int i) {
- ++predicate_count;
- return i != 2;
- },
- [&](int i) {
- ++projection_count;
- return i;
- });
-assert(ret.data() == a + 1);
-assert(*ret.data() == 2);
-assert(predicate_count == 3);
-assert(projection_count == 3);
-}
+ {
+ int a[] = {1, 1, 1};
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if_not(a, [](int) { return false; });
+ assert(ret.data() == a + 2);
+ }
+ }
-{
- int a[] = {1, 2, 3, 4};
- int predicate_count = 0;
- int projection_count = 0;
- std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if_not(
- a,
- [&](int i) {
- ++predicate_count;
- return i != 2;
- },
- [&](int i) {
- ++projection_count;
- return i;
- });
- assert(ret.data() == a + 1);
- assert(*ret.data() == 2);
- assert(predicate_count == 3);
- assert(projection_count == 3);
-}
-}
+ { // check that ranges::dangling is returned
+ [[maybe_unused]] std::same_as<std::ranges::dangling> auto ret =
+ std::ranges::find_last_if_not(std::array{1, 2}, [](int) { return true; });
+ }
-{// check that the return type of `iter::operator*` doesn't change
- {NonConstComparableValue a[] = {NonConstComparableValue{}};
-std::same_as<std::ranges::subrange<NonConstComparableValue*>> auto ret =
- std::ranges::find_last_if_not(a, a + 1, [](auto&& e) { return e != NonConstComparableValue{}; });
-assert(ret.data() == a);
-}
+ { // check that an iterator is returned with a borrowing range
+ int a[] = {1, 2, 3, 4};
+ std::same_as<std::ranges::subrange<int*>> auto ret =
+ std::ranges::find_last_if_not(std::views::all(a), [](int) { return false; });
+ assert(ret.data() == a + 3);
+ assert(*ret.data() == 4);
+ }
-{
- NonConstComparableValue a[] = {NonConstComparableValue{}};
- std::same_as<std::ranges::borrowed_subrange_t<NonConstComparableValue(&)[1]>> auto ret =
- std::ranges::find_last_if_not(a, [](auto&& e) { return e != NonConstComparableValue{}; });
- assert(ret.data() == a);
-}
-}
+ { // check that std::invoke is used
+
+ {
+ struct S {
+ int i;
+ };
+
+ S a[] = {S{1}, S{3}, S{2}};
+ std::same_as<std::ranges::borrowed_subrange_t<S(&)[3]>> auto ret = std::ranges::find_last_if_not(
+ a, [](int) { return true; }, &S::i);
+ assert(ret.data() == a + 3);
+ }
+
+ {
+ struct S {
+ int i;
+ };
+
+ S a[] = {S{1}, S{3}, S{2}};
+ std::same_as<std::ranges::subrange<S*>> auto ret = std::ranges::find_last_if_not(
+ a, a + 3, [](int) { return true; }, &S::i);
+ assert(ret.data() == a + 3);
+ }
+ }
-{ // check that the implicit conversion to bool works
+ { // count projection and predicate invocation count
+
+ {
+ int a[] = {1, 2, 3, 4};
+ int predicate_count = 0;
+ int projection_count = 0;
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if_not(
+ a,
+ a + 4,
+ [&](int i) {
+ ++predicate_count;
+ return i != 2;
+ },
+ [&](int i) {
+ ++projection_count;
+ return i;
+ });
+ assert(ret.data() == a + 1);
+ assert(*ret.data() == 2);
+ assert(predicate_count == 3);
+ assert(projection_count == 3);
+ }
+
+ {
+ int a[] = {1, 2, 3, 4};
+ int predicate_count = 0;
+ int projection_count = 0;
+ std::same_as<std::ranges::subrange<int*>> auto ret = std::ranges::find_last_if_not(
+ a,
+ [&](int i) {
+ ++predicate_count;
+ return i != 2;
+ },
+ [&](int i) {
+ ++projection_count;
+ return i;
+ });
+ assert(ret.data() == a + 1);
+ assert(*ret.data() == 2);
+ assert(predicate_count == 3);
+ assert(projection_count == 3);
+ }
+ }
- {
- int a[] = {1, 2, 3, 4};
- std::same_as<std::ranges::subrange<int*>> auto ret =
- std::ranges::find_last_if_not(a, a + 4, [](const int& i) { return BooleanTestable{i != 3}; });
- assert(ret.data() == a + 2);
+ { // check that the return type of `iter::operator*` doesn't change
+ {
+ NonConstComparableValue a[] = {NonConstComparableValue{}};
+ std::same_as<std::ranges::subrange<NonConstComparableValue*>> auto ret =
+ std::ranges::find_last_if_not(a, a + 1, [](auto&& e) { return e != NonConstComparableValue{}; });
+ assert(ret.data() == a);
+ }
+
+ {
+ NonConstComparableValue a[] = {NonConstComparableValue{}};
+ std::same_as<std::ranges::borrowed_subrange_t<NonConstComparableValue(&)[1]>> auto ret =
+ std::ranges::find_last_if_not(a, [](auto&& e) { return e != NonConstComparableValue{}; });
+ assert(ret.data() == a);
+ }
}
- {
- int a[] = {1, 2, 3, 4};
- std::same_as<std::ranges::subrange<int*>> auto ret =
- std::ranges::find_last_if_not(a, [](const int& b) { return BooleanTestable{b != 3}; });
- assert(ret.data() == a + 2);
+
+ { // check that the implicit conversion to bool works
+
+ {
+ int a[] = {1, 2, 3, 4};
+ std::same_as<std::ranges::subrange<int*>> auto ret =
+ std::ranges::find_last_if_not(a, a + 4, [](const int& i) { return BooleanTestable{i != 3}; });
+ assert(ret.data() == a + 2);
+ }
+ {
+ int a[] = {1, 2, 3, 4};
+ std::same_as<std::ranges::subrange<int*>> auto ret =
+ std::ranges::find_last_if_not(a, [](const int& b) { return BooleanTestable{b != 3}; });
+ assert(ret.data() == a + 2);
+ }
}
-}
-return true;
+ return true;
}
int main(int, char**) {
|
I will fix the formatting issues tomorrow :) |
I'm not sure about the format issues, it looks weird formatting at the same time for the tests. |
@phyBrackets Don't format all the tests. There's new infrastructure to check formatting and it behaves differently from our previous one, so it reports the tests as being mis-formatted. We'll fix that, but for now just ignore these failures. The |
696ddc1
to
b966734
Compare
Note that if you rebase onto main now, the Github Actions shouldn't complain spuriously anymore. It should only complain about stuff that you should fix for real. |
b966734
to
1a65a7e
Compare
} | ||
++__first; | ||
} | ||
return {__result, __last}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is not correct. __last
is of type _Sp
. You actually want to return the __first
here because it is the correct type _Ip
and it reaches the __last
at this point.
Do you have a test for this? If _Sp
is different type than _Ip
, this would fail.
|
||
template <class _Ip, class _Sp, class _Pred, class _Proj> | ||
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr subrange<_Ip> | ||
__find_last_if_impl(_Ip __first, _Sp __last, _Pred __pred, _Proj __proj) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I would say this is an algorithm that is worth do some optimisations depending on the types of the inputs. If the input models bidirectional_range && common_range
, you would search from the end, and you leave this implementation as a fallback if the inputs do not model the concept
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Also bidirectional_iterator<I> and assignable_from<I&, S&>
test_iterators<const int*>(); | ||
test_iterators<int*>(); | ||
test_iterators<bidirectional_iterator<int*>>(); | ||
test_iterators<forward_iterator<int*>>(); | ||
test_iterators<random_access_iterator<int*>>(); | ||
test_iterators<contiguous_iterator<int*>>(); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please have tests when sentinel is a different type than the iterator.
This applies for other tests
@phyBrackets Do you plan to continue working on this? Please let us know if you'd like someone else to pick up this patch (I can volunteer). |
Hi @var-const , yes I'll update it asap. Just get busy with my university exams and few other things. Apologies for the delay. |
Hello @phyBrackets and @var-const -- I am happy to help, too, if you'd like!! |
template <class _Ip, class _Sp, class _Pred, class _Proj> | ||
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr subrange<_Ip> | ||
__find_last_if_impl(_Ip __first, _Sp __last, _Pred __pred, _Proj __proj) { | ||
_Ip __result = __last; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This requires assignable_from<_Ip&, _Sp&>
, so we need to use ranges::next
.
_Ip __result = __last; | |
_Ip __result = ranges::next(__first, __last); |
sentinel_for<_Ip> _Sp, | ||
class _Proj = identity, | ||
indirect_unary_predicate<projected<_Ip, _Proj>> _Pred> | ||
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr subrange<_Ip> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please add tests for the [[nodiscard]]
extensions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd prefer it if we implemented find_last
and find_last_if
independently, instead of deferring to an implementation function.
|
||
template <class _Ip, class _Sp, class _Pred, class _Proj> | ||
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr subrange<_Ip> | ||
__find_last_if_impl(_Ip __first, _Sp __last, _Pred __pred, _Proj __proj) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
__find_last_if_impl(_Ip __first, _Sp __last, _Pred __pred, _Proj __proj) { | |
__find_last_if_impl(_Ip __first, _Sp __last, _Pred&& __pred, _Proj&& __proj) { |
We don't want to copy __pred
and __proj
, and references are nicer than std::reference_wrapper
.
Why? |
It simplifies the implementation so that we can directly call |
It also means we'll have to duplicate optimizations, which significantly increases the complexity of the implementation. Improving the code gen of |
I don’t think it simplifies the “implementation” of Libc++, but quite opposite, make it very complex. You can see the every optimisation overload is duplicated in this review. Even my code review comments to these optimisations are duplicated. I think you are effectively suggesting all library writers : duplicate code if you can because it release the burden of compiler optimisations and debugging tool. This just adds burden to library writers. |
I very much agree with @huixie90. Please revert to forwarding |
Agree with @huixie90 and @philnik777 -- code duplication is a big issue in our code base, we should try to keep it minimal. |
I thought I replied to this, but evidently not. Yes, I've got a patch for Clang that transforms |
requires indirect_binary_predicate<ranges::equal_to, projected<iterator_t<_Rp>, _Proj>, const _Tp*> | ||
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI constexpr borrowed_subrange_t<_Rp> | ||
operator()(_Rp&& __r, const _Tp& __value, _Proj __proj = {}) const { | ||
return operator()(ranges::begin(__r), ranges::end(__r), __value, __proj); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
return operator()(ranges::begin(__r), ranges::end(__r), __value, __proj); | |
[[clang::always_inline]] return operator()(ranges::begin(__r), ranges::end(__r), __value, std::ref(__proj)); |
If we're to keep this as a function call, then we should inline it, so as to not penalise the caller. We also need to not copy __proj
.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think we use always_inline anywhere in the library. If clang failed to inline this function call, I would file a bug report to clang
Hi @huixie90 apologies for taking long time, Can you please review the changes? I will make it correctly format and rebase. |
} else if constexpr (bidirectional_iterator<_Ip> && assignable_from<_Ip&, _Sp&>) { | ||
// For non-common but bidirectional ranges | ||
_Ip __result = ranges::next(__first, __last); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
To make sure ranges::next
is constant time, I think you can do
(random_access_iterator<_Ip> && sized_sentinel_for<_Sp, _Ip>) || (bidirectional_iterator<_Ip> && assignable_from<_Ip&, _Sp&>)
template <class _Ip, class _Sp, class _Pred, class _Proj> | ||
_LIBCPP_NODISCARD_EXT _LIBCPP_HIDE_FROM_ABI static constexpr subrange<_Ip> | ||
__find_last_if_impl(_Ip __first, _Sp __last, _Pred&& __pred, _Proj&& __proj) { | ||
if constexpr (common_range<_Ip> && bidirectional_range<_Ip>) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is incorrect. since here you passed in iterator and sentinel, not the range itself, so they will never be common_range
or bidirectional_range
. I think your second branch almost covers it except that instead of directly copying the last, it copies the first then assigns it to the last. If we want to keep this branch, I think the correct check is to check iterator and sentinel are the same type and the iterator models bidi iterator
} | ||
++__first; | ||
} | ||
return {__result, __original_first}; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am going to close this as inactive, since it hasn't been updated in a very long time and I want to avoid losing momentum on #99312. |
Follow up the discussion from https://reviews.llvm.org/D136522