From 576e045acd97c69c629f93eb5c350de9873afae4 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Tue, 6 Dec 2022 13:38:30 +0000 Subject: [PATCH 01/11] Implement P1223R5 find_last, et al. --- stl/inc/algorithm | 214 ++++++++++++++++++ stl/inc/yvals_core.h | 2 + tests/std/test.lst | 3 + .../P1223R5_ranges_alg_find_last/env.lst | 4 + .../P1223R5_ranges_alg_find_last/test.cpp | 70 ++++++ .../P1223R5_ranges_alg_find_last_if/env.lst | 4 + .../P1223R5_ranges_alg_find_last_if/test.cpp | 71 ++++++ .../env.lst | 4 + .../test.cpp | 76 +++++++ .../test.compile.pass.cpp | 14 ++ 10 files changed, 462 insertions(+) create mode 100644 tests/std/tests/P1223R5_ranges_alg_find_last/env.lst create mode 100644 tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp create mode 100644 tests/std/tests/P1223R5_ranges_alg_find_last_if/env.lst create mode 100644 tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp create mode 100644 tests/std/tests/P1223R5_ranges_alg_find_last_if_not/env.lst create mode 100644 tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 8253371f45..0491b44364 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2797,6 +2797,220 @@ namespace ranges { }; _EXPORT_STD inline constexpr _Fold_right_last_fn fold_right_last{_Not_quite_object::_Construct_tag{}}; + + class _Find_last_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Ty, class _Pj = identity> + requires indirect_binary_predicate, const _Ty*> + _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Sent, const _Ty& _Value, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Sent); + + auto [_UFirst, _ULast] = _Find_last_unchecked( + _Unwrap_iter<_Se>(_STD move(_First)), _Unwrap_sent<_It>(_STD move(_Sent)), _Value, _Pass_fn(_Proj)); + _It _Last{}; + _Seek_wrapped(_First, _STD move(_UFirst)); + _Seek_wrapped(_Last, _STD move(_ULast)); + return {_STD move(_First), _STD move(_Last)}; + } + + template + requires indirect_binary_predicate, _Pj>, const _Ty*> + _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()( + _Rng&& _Range, const _Ty& _Value, _Pj _Proj = {}) const { + auto _First = _RANGES begin(_Range); + auto [_UFirst, _ULast] = _Find_last_unchecked( + _Unwrap_range_iter<_Rng>(_STD move(_First)), _Uend(_Range), _Value, _Pass_fn(_Proj)); + iterator_t<_Rng> _Last{}; + _Seek_wrapped(_First, _STD move(_UFirst)); + _Seek_wrapped(_Last, _STD move(_ULast)); + return {_STD move(_First), _STD move(_Last)}; + } + + private: + template + _NODISCARD static constexpr subrange<_It> _Find_last_unchecked( + _It _First, _Se _Last, const _Ty& _Value, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_binary_predicate, const _Ty*>); + + if constexpr (_Bidi_common<_It, _Se>) { + for (auto _Result = _Last; _Result != _First;) { + if (_STD invoke(_Proj, *--_Result) == _Value) { + return {_STD move(_Result), _STD move(_Last)}; + } + } + return {_Last, _Last}; + } else if constexpr (same_as<_It, _Se>) { + auto _Result = _Last; + for (; _First != _Last; ++_First) { + if (_STD invoke(_Proj, *_First) == _Value) { + _Result = _First; + } + } + return {_STD move(_Result), _STD move(_Last)}; + } else { + _It _Result{}; + for (;;) { + if (_STD invoke(_Proj, *_First) == _Value) { + _Result = _First; + } + if (++_First == _Last) { + if (_Result == _It{}) { + _Result = _First; + } + break; + } + } + return {_STD move(_Result), _STD move(_First)}; + } + } + }; + + _EXPORT_STD inline constexpr _Find_last_fn find_last{_Not_quite_object::_Construct_tag{}}; + + class _Find_last_if_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Sent, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Sent); + + auto [_UFirst, _ULast] = _Find_last_if_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), + _Unwrap_sent<_It>(_STD move(_Sent)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + _It _Last{}; + _Seek_wrapped(_First, _STD move(_UFirst)); + _Seek_wrapped(_Last, _STD move(_ULast)); + return {_STD move(_First), _STD move(_Last)}; + } + template , _Pj>> _Pr> + constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + auto _First = _RANGES begin(_Range); + auto [_UFirst, _ULast] = _Find_last_if_unchecked( + _Unwrap_range_iter<_Rng>(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + iterator_t<_Rng> _Last{}; + _Seek_wrapped(_First, _STD move(_UFirst)); + _Seek_wrapped(_Last, _STD move(_ULast)); + return {_STD move(_First), _STD move(_Last)}; + } + + private: + template + _NODISCARD static constexpr subrange<_It> _Find_last_if_unchecked(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + if constexpr (_Bidi_common<_It, _Se>) { + for (auto _Result = _Last; _Result != _First;) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *--_Result))) { + return {_STD move(_Result), _STD move(_Last)}; + } + } + return {_Last, _Last}; + } else if constexpr (same_as<_It, _Se>) { + auto _Result = _Last; + for (; _First != _Last; ++_First) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + _Result = _First; + } + } + return {_STD move(_Result), _STD move(_Last)}; + } else { + _It _Result{}; + for (;;) { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + _Result = _First; + } + if (++_First == _Last) { + if (_Result == _It{}) { + _Result = _First; + } + break; + } + } + return {_STD move(_Result), _STD move(_First)}; + } + } + }; + + _EXPORT_STD inline constexpr _Find_last_if_fn find_last_if{_Not_quite_object::_Construct_tag{}}; + + class _Find_last_if_not_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + template _Se, class _Pj = identity, + indirect_unary_predicate> _Pr> + _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Sent, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Sent); + + auto [_UFirst, _ULast] = _Find_last_if_not_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), + _Unwrap_sent<_It>(_STD move(_Sent)), _Pass_fn(_Pred), _Pass_fn(_Proj)); + _It _Last{}; + _Seek_wrapped(_First, _STD move(_UFirst)); + _Seek_wrapped(_Last, _STD move(_ULast)); + return {_STD move(_First), _STD move(_Last)}; + } + template , _Pj>> _Pr> + constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + auto _First = _RANGES begin(_Range); + auto [_UFirst, _ULast] = _Find_last_if_not_unchecked( + _Unwrap_range_iter<_Rng>(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + iterator_t<_Rng> _Last{}; + _Seek_wrapped(_First, _STD move(_UFirst)); + _Seek_wrapped(_Last, _STD move(_ULast)); + return {_STD move(_First), _STD move(_Last)}; + } + + private: + template + _NODISCARD static constexpr subrange<_It> _Find_last_if_not_unchecked( + _It _First, _Se _Last, _Pr _Pred, _Pj _Proj) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); + _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); + + if constexpr (_Bidi_common<_It, _Se>) { + for (auto _Result = _Last; _Result != _First;) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *--_Result))) { + return {_STD move(_Result), _STD move(_Last)}; + } + } + return {_Last, _Last}; + } else if constexpr (same_as<_It, _Se>) { + auto _Result = _Last; + for (; _First != _Last; ++_First) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + _Result = _First; + } + } + return {_STD move(_Result), _STD move(_Last)}; + } else { + _It _Result{}; + for (;;) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + _Result = _First; + } + if (++_First == _Last) { + if (_Result == _It{}) { + _Result = _First; + } + break; + } + } + return {_STD move(_Result), _STD move(_First)}; + } + } + }; + + _EXPORT_STD inline constexpr _Find_last_if_not_fn find_last_if_not{_Not_quite_object::_Construct_tag{}}; #endif // _HAS_CXX23 } // namespace ranges #endif // __cpp_lib_concepts diff --git a/stl/inc/yvals_core.h b/stl/inc/yvals_core.h index 24d25b184f..dd54727214 100644 --- a/stl/inc/yvals_core.h +++ b/stl/inc/yvals_core.h @@ -307,6 +307,7 @@ // P1132R7 out_ptr(), inout_ptr() // P1147R1 Printing volatile Pointers // P1206R7 Conversions From Ranges To Containers +// P1223R5 ranges::find_last, ranges::find_last_if, ranges::find_last_if_not // P1272R4 byteswap() // P1328R1 constexpr type_info::operator==() // P1413R3 Deprecate aligned_storage And aligned_union @@ -1677,6 +1678,7 @@ _EMIT_STL_ERROR(STL1004, "C++98 unexpected() is incompatible with C++23 unexpect #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 // per LWG-3807 #define __cpp_lib_ranges_fold 202207L #define __cpp_lib_ranges_iota 202202L #define __cpp_lib_ranges_join_with 202202L diff --git a/tests/std/test.lst b/tests/std/test.lst index 9af561348b..0b1b24d248 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -529,6 +529,9 @@ tests\P1206R7_vector_assign_range tests\P1206R7_vector_from_range tests\P1206R7_vector_insert_range tests\P1208R6_source_location +tests\P1223R5_find_last +tests\P1223R5_find_last_if +tests\P1223R5_find_last_if_not tests\P1272R4_byteswap tests\P1423R3_char8_t_remediation tests\P1425R4_queue_stack_constructors diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last/env.lst b/tests/std/tests/P1223R5_ranges_alg_find_last/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P1223R5_ranges_alg_find_last/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp new file mode 100644 index 0000000000..d40194e220 --- /dev/null +++ b/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp @@ -0,0 +1,70 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +// Validate dangling story +STATIC_ASSERT(same_as{}, 42)), ranges::dangling>); +STATIC_ASSERT(same_as{}, 42)), ranges::subrange>); + +template +constexpr void check_value(const T& found, const U& value) { + if constexpr (same_as) { + assert(found.first == value); + } else { + assert(found.peek().first == value); + } +} + +struct instantiator { + static constexpr P haystack[3] = {{0, 42}, {2, 42}, {4, 42}}; + + template + static constexpr void call() { + using ranges::find_last, ranges::common_range, ranges::iterator_t; + + for (const auto& [value, _] : haystack) { + { // Validate range overload [found case] + Read wrapped_input{haystack}; + const auto result = find_last(wrapped_input, value, get_first); + STATIC_ASSERT(same_as, iterator_t>); + STATIC_ASSERT(common_range); + check_value(result.front(), value); + } + { // Validate iterator + sentinel overload [found case] + Read wrapped_input{haystack}; + auto result = find_last(wrapped_input.begin(), wrapped_input.end(), value, get_first); + STATIC_ASSERT(same_as, iterator_t>); + STATIC_ASSERT(common_range); + check_value(result.front(), value); + } + } + { // Validate range overload [not found case] + Read wrapped_input{haystack}; + auto result = find_last(wrapped_input, 42, get_first); + STATIC_ASSERT(same_as, iterator_t>); + STATIC_ASSERT(common_range); + assert(result.begin() == wrapped_input.end()); + } + { // Validate iterator + sentinel overload [not found case] + Read wrapped_input{haystack}; + auto result = find_last(wrapped_input.begin(), wrapped_input.end(), 42, get_first); + STATIC_ASSERT(same_as, iterator_t>); + STATIC_ASSERT(common_range); + assert(result.begin() == wrapped_input.end()); + } + } +}; + +int main() { + STATIC_ASSERT((test_fwd(), true)); + test_fwd(); +} diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if/env.lst b/tests/std/tests/P1223R5_ranges_alg_find_last_if/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp new file mode 100644 index 0000000000..e0b995c8d2 --- /dev/null +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp @@ -0,0 +1,71 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +constexpr auto matches = [](const int val) { return val == 42; }; +constexpr auto equals = [](auto x) { return [x](auto&& y) { return y == x; }; }; + +// Validate dangling story +STATIC_ASSERT(same_as{}, matches)), ranges::dangling>); +STATIC_ASSERT(same_as{}, matches)), ranges::subrange>); + +template +constexpr void check_value(const T& found, const U& value) { + if constexpr (same_as) { + assert(found.first == value); + } else { + assert(found.peek().first == value); + } +} + +struct instantiator { + static constexpr P haystack[3] = {{0, 42}, {2, 42}, {4, 42}}; + + template + static constexpr void call() { + using ranges::find_last_if, ranges::common_range, ranges::iterator_t; + + for (const auto& [value, _] : haystack) { + { // Validate range overload [found case] + Read wrapped_input{haystack}; + auto result = find_last_if(wrapped_input, equals(value), get_first); + STATIC_ASSERT(same_as, iterator_t>); + STATIC_ASSERT(common_range); + check_value(result.front(), value); + } + { // Validate iterator + sentinel overload [found case] + Read wrapped_input{haystack}; + auto result = find_last_if(wrapped_input.begin(), wrapped_input.end(), equals(value), get_first); + STATIC_ASSERT(same_as, iterator_t>); + STATIC_ASSERT(common_range); + check_value(result.front(), value); + } + } + { // Validate range overload [not found case] + Read wrapped_input{haystack}; + auto result = find_last_if(wrapped_input, equals(42), get_first); + STATIC_ASSERT(same_as, iterator_t>); + assert(result.begin() == wrapped_input.end()); + } + { // Validate iterator + sentinel overload [not found case] + Read wrapped_input{haystack}; + auto result = find_last_if(wrapped_input.begin(), wrapped_input.end(), equals(42), get_first); + STATIC_ASSERT(same_as, iterator_t>); + assert(result.begin() == wrapped_input.end()); + } + } +}; + +int main() { + STATIC_ASSERT((test_fwd(), true)); + test_fwd(); +} diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/env.lst b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/env.lst new file mode 100644 index 0000000000..18e2d7c71e --- /dev/null +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp new file mode 100644 index 0000000000..3cdfd98676 --- /dev/null +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp @@ -0,0 +1,76 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +#include +using namespace std; +using P = pair; + +constexpr auto matches = [](const int val) { return val == 42; }; +constexpr auto equals = [](auto x) { return [x](auto&& y) { return y == x; }; }; + +// Validate dangling story +STATIC_ASSERT(same_as{}, matches)), ranges::dangling>); +STATIC_ASSERT(same_as{}, matches)), ranges::subrange>); + +template +constexpr void check_value(const T& found, const U& value) { + if constexpr (same_as) { + assert(found.first == value); + } else { + assert(found.peek().first == value); + } +} + +struct instantiator { + template + static constexpr void call() { + using ranges::find_last_if_not, ranges::common_range, ranges::iterator_t; + P haystack[3] = {{0, 13}, {0, 13}, {0, 13}}; + + for (auto& [value, _] : haystack) { + value = 42; + { // Validate range overload [found case] + Read wrapped_input{haystack}; + auto result = find_last_if_not(wrapped_input, equals(0), get_first); + STATIC_ASSERT(same_as, iterator_t>); + STATIC_ASSERT(common_range); + check_value(result.front(), 42); + } + { // Validate iterator + sentinel overload [found case] + Read wrapped_input{haystack}; + auto result = find_last_if_not(wrapped_input.begin(), wrapped_input.end(), equals(0), get_first); + STATIC_ASSERT(same_as, iterator_t>); + STATIC_ASSERT(common_range); + check_value(result.front(), 42); + } + value = 0; + } + { // Validate range overload [not found case] + Read wrapped_input{haystack}; + auto result = find_last_if_not(wrapped_input, equals(13), get_second); + STATIC_ASSERT(same_as, iterator_t>); + STATIC_ASSERT(common_range); + assert(result.begin() == wrapped_input.end()); + } + { // Validate iterator + sentinel overload [not found case] + Read wrapped_input{haystack}; + auto result = find_last_if_not(wrapped_input.begin(), wrapped_input.end(), equals(13), get_second); + STATIC_ASSERT(same_as, iterator_t>); + STATIC_ASSERT(common_range); + assert(result.begin() == wrapped_input.end()); + } + } +}; + +int main() { + // causes ICE + // STATIC_ASSERT((test_fwd(), true)); + test_fwd(); +} diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index 59b2ebcc03..ec3a5fc428 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1574,6 +1574,20 @@ STATIC_ASSERT(__cpp_lib_ranges_contains == 202207L); #endif #endif +#if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 +#ifndef __cpp_lib_ranges_find_last // per LWG-3807 +#error __cpp_lib_ranges_find_last is not defined +#elif __cpp_lib_ranges_find_last != 202207L +#error __cpp_lib_ranges_find_last is not 201703L +#else +STATIC_ASSERT(__cpp_lib_ranges_find_last == 202207L); +#endif +#else +#ifdef __cpp_lib_ranges_find_last +#error __cpp_lib_ranges_find_last is defined +#endif +#endif + #if _HAS_CXX23 && defined(__cpp_lib_concepts) // TRANSITION, GH-395 #ifndef __cpp_lib_ranges_fold #error __cpp_lib_ranges_fold is not defined From b07827422cddb0154645f10074cb8475f941cfc4 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Fri, 9 Dec 2022 09:42:08 +0000 Subject: [PATCH 02/11] Fix whoopsie daisies --- stl/inc/algorithm | 5 +++-- tests/std/test.lst | 6 +++--- .../VSO_0157762_feature_test_macros/test.compile.pass.cpp | 2 +- 3 files changed, 7 insertions(+), 6 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 0491b44364..199dcfcddc 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2857,6 +2857,7 @@ namespace ranges { if (_STD invoke(_Proj, *_First) == _Value) { _Result = _First; } + if (++_First == _Last) { if (_Result == _It{}) { _Result = _First; @@ -2889,7 +2890,7 @@ namespace ranges { } template , _Pj>> _Pr> - constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { auto _First = _RANGES begin(_Range); auto [_UFirst, _ULast] = _Find_last_if_unchecked( _Unwrap_range_iter<_Rng>(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); @@ -2959,7 +2960,7 @@ namespace ranges { } template , _Pj>> _Pr> - constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { + _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { auto _First = _RANGES begin(_Range); auto [_UFirst, _ULast] = _Find_last_if_not_unchecked( _Unwrap_range_iter<_Rng>(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); diff --git a/tests/std/test.lst b/tests/std/test.lst index 0b1b24d248..44a0e5a6d9 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -529,9 +529,9 @@ tests\P1206R7_vector_assign_range tests\P1206R7_vector_from_range tests\P1206R7_vector_insert_range tests\P1208R6_source_location -tests\P1223R5_find_last -tests\P1223R5_find_last_if -tests\P1223R5_find_last_if_not +tests\P1223R5_ranges_alg_find_last +tests\P1223R5_ranges_alg_find_last_if +tests\P1223R5_ranges_alg_find_last_if_not tests\P1272R4_byteswap tests\P1423R3_char8_t_remediation tests\P1425R4_queue_stack_constructors diff --git a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp index ec3a5fc428..4ba8d60af1 100644 --- a/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp +++ b/tests/std/tests/VSO_0157762_feature_test_macros/test.compile.pass.cpp @@ -1578,7 +1578,7 @@ STATIC_ASSERT(__cpp_lib_ranges_contains == 202207L); #ifndef __cpp_lib_ranges_find_last // per LWG-3807 #error __cpp_lib_ranges_find_last is not defined #elif __cpp_lib_ranges_find_last != 202207L -#error __cpp_lib_ranges_find_last is not 201703L +#error __cpp_lib_ranges_find_last is not 202207L #else STATIC_ASSERT(__cpp_lib_ranges_find_last == 202207L); #endif From cba7e786cf5e13630893b678ac1b6a7a36f0b358 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Fri, 9 Dec 2022 10:12:34 +0000 Subject: [PATCH 03/11] Properly rewrap --- stl/inc/algorithm | 48 +++++++++++++++-------------------------------- 1 file changed, 15 insertions(+), 33 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 199dcfcddc..4191c30d6a 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2807,25 +2807,19 @@ namespace ranges { _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Sent, const _Ty& _Value, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Sent); - auto [_UFirst, _ULast] = _Find_last_unchecked( + auto _UResult = _Find_last_unchecked( _Unwrap_iter<_Se>(_STD move(_First)), _Unwrap_sent<_It>(_STD move(_Sent)), _Value, _Pass_fn(_Proj)); - _It _Last{}; - _Seek_wrapped(_First, _STD move(_UFirst)); - _Seek_wrapped(_Last, _STD move(_ULast)); - return {_STD move(_First), _STD move(_Last)}; + return _Rewrap_subrange>(_First, _STD move(_UResult)); } template requires indirect_binary_predicate, _Pj>, const _Ty*> _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()( _Rng&& _Range, const _Ty& _Value, _Pj _Proj = {}) const { - auto _First = _RANGES begin(_Range); - auto [_UFirst, _ULast] = _Find_last_unchecked( + auto _First = _RANGES begin(_Range); + auto _UResult = _Find_last_unchecked( _Unwrap_range_iter<_Rng>(_STD move(_First)), _Uend(_Range), _Value, _Pass_fn(_Proj)); - iterator_t<_Rng> _Last{}; - _Seek_wrapped(_First, _STD move(_UFirst)); - _Seek_wrapped(_Last, _STD move(_ULast)); - return {_STD move(_First), _STD move(_Last)}; + return _Rewrap_subrange>(_First, _STD move(_UResult)); } private: @@ -2881,23 +2875,17 @@ namespace ranges { _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Sent, _Pr _Pred, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Sent); - auto [_UFirst, _ULast] = _Find_last_if_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), + auto _UResult = _Find_last_if_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), _Unwrap_sent<_It>(_STD move(_Sent)), _Pass_fn(_Pred), _Pass_fn(_Proj)); - _It _Last{}; - _Seek_wrapped(_First, _STD move(_UFirst)); - _Seek_wrapped(_Last, _STD move(_ULast)); - return {_STD move(_First), _STD move(_Last)}; + return _Rewrap_subrange>(_First, _STD move(_UResult)); } template , _Pj>> _Pr> _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { - auto _First = _RANGES begin(_Range); - auto [_UFirst, _ULast] = _Find_last_if_unchecked( + auto _First = _RANGES begin(_Range); + auto _UResult = _Find_last_if_unchecked( _Unwrap_range_iter<_Rng>(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); - iterator_t<_Rng> _Last{}; - _Seek_wrapped(_First, _STD move(_UFirst)); - _Seek_wrapped(_Last, _STD move(_ULast)); - return {_STD move(_First), _STD move(_Last)}; + return _Rewrap_subrange>(_First, _STD move(_UResult)); } private: @@ -2951,23 +2939,17 @@ namespace ranges { _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Sent, _Pr _Pred, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Sent); - auto [_UFirst, _ULast] = _Find_last_if_not_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), + auto _UResult = _Find_last_if_not_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), _Unwrap_sent<_It>(_STD move(_Sent)), _Pass_fn(_Pred), _Pass_fn(_Proj)); - _It _Last{}; - _Seek_wrapped(_First, _STD move(_UFirst)); - _Seek_wrapped(_Last, _STD move(_ULast)); - return {_STD move(_First), _STD move(_Last)}; + return _Rewrap_subrange>(_First, _STD move(_UResult)); } template , _Pj>> _Pr> _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { - auto _First = _RANGES begin(_Range); - auto [_UFirst, _ULast] = _Find_last_if_not_unchecked( + auto _First = _RANGES begin(_Range); + auto _UResult = _Find_last_if_not_unchecked( _Unwrap_range_iter<_Rng>(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); - iterator_t<_Rng> _Last{}; - _Seek_wrapped(_First, _STD move(_UFirst)); - _Seek_wrapped(_Last, _STD move(_ULast)); - return {_STD move(_First), _STD move(_Last)}; + return _Rewrap_subrange>(_First, _STD move(_UResult)); } private: From 03174d6bc6f47cf576bfc7840a45f563f22c5d39 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Fri, 9 Dec 2022 12:30:58 +0000 Subject: [PATCH 04/11] Utilise _Get_final_iterator_unwrapped --- stl/inc/algorithm | 96 ++++++++++++++++++++++++++++++++++------------- 1 file changed, 69 insertions(+), 27 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 4191c30d6a..96f46d0749 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2804,22 +2804,34 @@ namespace ranges { template _Se, class _Ty, class _Pj = identity> requires indirect_binary_predicate, const _Ty*> - _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Sent, const _Ty& _Value, _Pj _Proj = {}) const { - _Adl_verify_range(_First, _Sent); + _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Last, const _Ty& _Value, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); - auto _UResult = _Find_last_unchecked( - _Unwrap_iter<_Se>(_STD move(_First)), _Unwrap_sent<_It>(_STD move(_Sent)), _Value, _Pass_fn(_Proj)); - return _Rewrap_subrange>(_First, _STD move(_UResult)); + if constexpr (bidirectional_iterator<_It>) { + auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + auto _UResult = _Find_last_unchecked(_STD move(_UFirst), _STD move(_ULast), _Value, _Pass_fn(_Proj)); + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } else { + auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); + auto _ULast = _Unwrap_sent<_It>(_STD move(_Last)); + auto _UResult = _Find_last_unchecked(_STD move(_UFirst), _STD move(_ULast), _Value, _Pass_fn(_Proj)); + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } } template requires indirect_binary_predicate, _Pj>, const _Ty*> _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()( _Rng&& _Range, const _Ty& _Value, _Pj _Proj = {}) const { - auto _First = _RANGES begin(_Range); - auto _UResult = _Find_last_unchecked( - _Unwrap_range_iter<_Rng>(_STD move(_First)), _Uend(_Range), _Value, _Pass_fn(_Proj)); - return _Rewrap_subrange>(_First, _STD move(_UResult)); + if constexpr (bidirectional_range<_Rng>) { + auto _UResult = _Find_last_unchecked( + _Ubegin(_Range), _Get_final_iterator_unwrapped(_Range), _Value, _Pass_fn(_Proj)); + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } else { + auto _UResult = _Find_last_unchecked(_Ubegin(_Range), _Uend(_Range), _Value, _Pass_fn(_Proj)); + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } } private: @@ -2872,20 +2884,35 @@ namespace ranges { template _Se, class _Pj = identity, indirect_unary_predicate> _Pr> - _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Sent, _Pr _Pred, _Pj _Proj = {}) const { - _Adl_verify_range(_First, _Sent); + _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); - auto _UResult = _Find_last_if_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), - _Unwrap_sent<_It>(_STD move(_Sent)), _Pass_fn(_Pred), _Pass_fn(_Proj)); - return _Rewrap_subrange>(_First, _STD move(_UResult)); + if constexpr (bidirectional_iterator<_It>) { + auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + auto _UResult = + _Find_last_if_unchecked(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } else { + auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); + auto _ULast = _Unwrap_sent<_It>(_STD move(_Last)); + auto _UResult = + _Find_last_if_unchecked(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } } template , _Pj>> _Pr> _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { - auto _First = _RANGES begin(_Range); - auto _UResult = _Find_last_if_unchecked( - _Unwrap_range_iter<_Rng>(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); - return _Rewrap_subrange>(_First, _STD move(_UResult)); + if constexpr (bidirectional_range<_Rng>) { + auto _UResult = _Find_last_if_unchecked( + _Ubegin(_Range), _Get_final_iterator_unwrapped(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } else { + auto _UResult = + _Find_last_if_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } } private: @@ -2936,20 +2963,35 @@ namespace ranges { template _Se, class _Pj = identity, indirect_unary_predicate> _Pr> - _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Sent, _Pr _Pred, _Pj _Proj = {}) const { - _Adl_verify_range(_First, _Sent); + _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { + _Adl_verify_range(_First, _Last); - auto _UResult = _Find_last_if_not_unchecked(_Unwrap_iter<_Se>(_STD move(_First)), - _Unwrap_sent<_It>(_STD move(_Sent)), _Pass_fn(_Pred), _Pass_fn(_Proj)); - return _Rewrap_subrange>(_First, _STD move(_UResult)); + if constexpr (bidirectional_iterator<_It>) { + auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + auto _UResult = _Find_last_if_not_unchecked( + _STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } else { + auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); + auto _ULast = _Unwrap_sent<_It>(_STD move(_Last)); + auto _UResult = _Find_last_if_not_unchecked( + _STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_subrange>(_First, _STD move(_UResult)); + } } template , _Pj>> _Pr> _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { - auto _First = _RANGES begin(_Range); - auto _UResult = _Find_last_if_not_unchecked( - _Unwrap_range_iter<_Rng>(_STD move(_First)), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); - return _Rewrap_subrange>(_First, _STD move(_UResult)); + if constexpr (bidirectional_range<_Rng>) { + auto _UResult = _Find_last_if_not_unchecked( + _Ubegin(_Range), _Get_final_iterator_unwrapped(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } else { + auto _UResult = + _Find_last_if_not_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); + return _Rewrap_subrange>(_Range, _STD move(_UResult)); + } } private: From c6282cd63766298b9821681895fed8c9ed4b1907 Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Sat, 10 Dec 2022 10:44:25 +0000 Subject: [PATCH 05/11] Correct terrible case --- stl/inc/algorithm | 49 ++++++++++--------- .../P1223R5_ranges_alg_find_last/test.cpp | 8 +++ .../P1223R5_ranges_alg_find_last_if/test.cpp | 8 +++ .../test.cpp | 8 +++ 4 files changed, 51 insertions(+), 22 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 96f46d0749..f529a1fa61 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2858,18 +2858,19 @@ namespace ranges { } return {_STD move(_Result), _STD move(_Last)}; } else { - _It _Result{}; - for (;;) { - if (_STD invoke(_Proj, *_First) == _Value) { - _Result = _First; - } - - if (++_First == _Last) { - if (_Result == _It{}) { + _It _Result = _First; + bool _Found = false; + for (;; ++_First) { + if (_First == _Last) { + if (!_Found) { _Result = _First; } break; } + if (_STD invoke(_Proj, *_First) == _Value) { + _Result = _First; + _Found = true; + } } return {_STD move(_Result), _STD move(_First)}; } @@ -2938,17 +2939,19 @@ namespace ranges { } return {_STD move(_Result), _STD move(_Last)}; } else { - _It _Result{}; - for (;;) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { - _Result = _First; - } - if (++_First == _Last) { - if (_Result == _It{}) { + _It _Result = _First; + bool _Found = false; + for (;; ++_First) { + if (_First == _Last) { + if (!_Found) { _Result = _First; } break; } + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + _Result = _First; + _Found = true; + } } return {_STD move(_Result), _STD move(_First)}; } @@ -3018,17 +3021,19 @@ namespace ranges { } return {_STD move(_Result), _STD move(_Last)}; } else { - _It _Result{}; - for (;;) { - if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { - _Result = _First; - } - if (++_First == _Last) { - if (_Result == _It{}) { + _It _Result = _First; + bool _Found = false; + for (;; ++_First) { + if (_First == _Last) { + if (!_Found) { _Result = _First; } break; } + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + _Result = _First; + _Found = true; + } } return {_STD move(_Result), _STD move(_First)}; } diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp index d40194e220..a8ce3e3e4c 100644 --- a/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp +++ b/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -61,6 +62,13 @@ struct instantiator { STATIC_ASSERT(common_range); assert(result.begin() == wrapped_input.end()); } + { // Validate empty range + Read wrapped_empty{span{}}; + auto iter_result = find_last(wrapped_empty.begin(), wrapped_empty.end(), 0, get_first); + auto range_result = find_last(wrapped_empty, 0, get_first); + assert(iter_result.begin() == wrapped_empty.end()); + assert(range_result.begin() == wrapped_empty.end()); + } } }; diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp index e0b995c8d2..c14453ae07 100644 --- a/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp @@ -5,6 +5,7 @@ #include #include #include +#include #include #include @@ -62,6 +63,13 @@ struct instantiator { STATIC_ASSERT(same_as, iterator_t>); assert(result.begin() == wrapped_input.end()); } + { // Validate empty range + Read wrapped_empty{span{}}; + auto iter_result = find_last_if(wrapped_empty.begin(), wrapped_empty.end(), equals(0), get_first); + auto range_result = find_last_if(wrapped_empty, equals(0), get_first); + assert(iter_result.begin() == wrapped_empty.end()); + assert(range_result.begin() == wrapped_empty.end()); + } } }; diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp index 3cdfd98676..cba4872a91 100644 --- a/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -66,6 +67,13 @@ struct instantiator { STATIC_ASSERT(common_range); assert(result.begin() == wrapped_input.end()); } + { // Validate empty range + Read wrapped_empty{span{}}; + auto iter_result = find_last_if_not(wrapped_empty.begin(), wrapped_empty.end(), equals(0), get_first); + auto range_result = find_last_if_not(wrapped_empty, equals(0), get_first); + assert(iter_result.begin() == wrapped_empty.end()); + assert(range_result.begin() == wrapped_empty.end()); + } } }; From 938e09eb9491d7fd4823facbc01770800025cb3d Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 12 Dec 2022 20:02:51 -0800 Subject: [PATCH 06/11] Add newlines. --- stl/inc/algorithm | 3 +++ 1 file changed, 3 insertions(+) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index f529a1fa61..a0a9ba1fe9 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2867,6 +2867,7 @@ namespace ranges { } break; } + if (_STD invoke(_Proj, *_First) == _Value) { _Result = _First; _Found = true; @@ -2948,6 +2949,7 @@ namespace ranges { } break; } + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { _Result = _First; _Found = true; @@ -3030,6 +3032,7 @@ namespace ranges { } break; } + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { _Result = _First; _Found = true; From 8641fb67f7bac4261be34d451d1ae7a6fb17033c Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 12 Dec 2022 20:52:15 -0800 Subject: [PATCH 07/11] This ICE doesn't appear to repro anymore. --- tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp index cba4872a91..f017b3be1f 100644 --- a/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp @@ -78,7 +78,6 @@ struct instantiator { }; int main() { - // causes ICE - // STATIC_ASSERT((test_fwd(), true)); + STATIC_ASSERT((test_fwd(), true)); test_fwd(); } From 20ae24cfd20cfc22585371f559be351df7231841 Mon Sep 17 00:00:00 2001 From: "Stephan T. Lavavej" Date: Mon, 12 Dec 2022 21:49:57 -0800 Subject: [PATCH 08/11] Add test coverage for multiple values. For find_last and find_last_if, this transformation is easy - add additional values with a `.second` component of `1729`, which allows us to verify that we always find the last one. The find_last_if_not test was following the existing find_if_not test, but that's difficult to adapt to "make sure we find the last one". So I've simply taken the find_last_if test and inverted the predicates - not not cat is cat. --- .../P1223R5_ranges_alg_find_last/test.cpp | 4 +- .../P1223R5_ranges_alg_find_last_if/test.cpp | 4 +- .../test.cpp | 37 +++++++++---------- 3 files changed, 24 insertions(+), 21 deletions(-) diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp index a8ce3e3e4c..754de9b10d 100644 --- a/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp +++ b/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp @@ -20,13 +20,15 @@ template constexpr void check_value(const T& found, const U& value) { if constexpr (same_as) { assert(found.first == value); + assert(found.second == 1729); } else { assert(found.peek().first == value); + assert(found.peek().second == 1729); } } struct instantiator { - static constexpr P haystack[3] = {{0, 42}, {2, 42}, {4, 42}}; + static constexpr P haystack[6] = {{0, 42}, {2, 42}, {4, 42}, {0, 1729}, {2, 1729}, {4, 1729}}; template static constexpr void call() { diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp index c14453ae07..8137bc6c17 100644 --- a/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp @@ -23,13 +23,15 @@ template constexpr void check_value(const T& found, const U& value) { if constexpr (same_as) { assert(found.first == value); + assert(found.second == 1729); } else { assert(found.peek().first == value); + assert(found.peek().second == 1729); } } struct instantiator { - static constexpr P haystack[3] = {{0, 42}, {2, 42}, {4, 42}}; + static constexpr P haystack[6] = {{0, 42}, {2, 42}, {4, 42}, {0, 1729}, {2, 1729}, {4, 1729}}; template static constexpr void call() { diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp index f017b3be1f..adb33ee591 100644 --- a/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp @@ -2,7 +2,6 @@ // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception #include -#include #include #include #include @@ -13,64 +12,64 @@ using namespace std; using P = pair; -constexpr auto matches = [](const int val) { return val == 42; }; -constexpr auto equals = [](auto x) { return [x](auto&& y) { return y == x; }; }; +constexpr auto not_matches = [](const int val) { return val != 42; }; +constexpr auto not_equals = [](auto x) { return [x](auto&& y) { return y != x; }; }; // Validate dangling story -STATIC_ASSERT(same_as{}, matches)), ranges::dangling>); -STATIC_ASSERT(same_as{}, matches)), ranges::subrange>); +STATIC_ASSERT(same_as{}, not_matches)), ranges::dangling>); +STATIC_ASSERT(same_as{}, not_matches)), ranges::subrange>); template constexpr void check_value(const T& found, const U& value) { if constexpr (same_as) { assert(found.first == value); + assert(found.second == 1729); } else { assert(found.peek().first == value); + assert(found.peek().second == 1729); } } struct instantiator { + static constexpr P haystack[6] = {{0, 42}, {2, 42}, {4, 42}, {0, 1729}, {2, 1729}, {4, 1729}}; + template static constexpr void call() { using ranges::find_last_if_not, ranges::common_range, ranges::iterator_t; - P haystack[3] = {{0, 13}, {0, 13}, {0, 13}}; - for (auto& [value, _] : haystack) { - value = 42; + for (const auto& [value, _] : haystack) { { // Validate range overload [found case] Read wrapped_input{haystack}; - auto result = find_last_if_not(wrapped_input, equals(0), get_first); + auto result = find_last_if_not(wrapped_input, not_equals(value), get_first); STATIC_ASSERT(same_as, iterator_t>); STATIC_ASSERT(common_range); - check_value(result.front(), 42); + check_value(result.front(), value); } { // Validate iterator + sentinel overload [found case] Read wrapped_input{haystack}; - auto result = find_last_if_not(wrapped_input.begin(), wrapped_input.end(), equals(0), get_first); + auto result = + find_last_if_not(wrapped_input.begin(), wrapped_input.end(), not_equals(value), get_first); STATIC_ASSERT(same_as, iterator_t>); STATIC_ASSERT(common_range); - check_value(result.front(), 42); + check_value(result.front(), value); } - value = 0; } { // Validate range overload [not found case] Read wrapped_input{haystack}; - auto result = find_last_if_not(wrapped_input, equals(13), get_second); + auto result = find_last_if_not(wrapped_input, not_equals(42), get_first); STATIC_ASSERT(same_as, iterator_t>); - STATIC_ASSERT(common_range); assert(result.begin() == wrapped_input.end()); } { // Validate iterator + sentinel overload [not found case] Read wrapped_input{haystack}; - auto result = find_last_if_not(wrapped_input.begin(), wrapped_input.end(), equals(13), get_second); + auto result = find_last_if_not(wrapped_input.begin(), wrapped_input.end(), not_equals(42), get_first); STATIC_ASSERT(same_as, iterator_t>); - STATIC_ASSERT(common_range); assert(result.begin() == wrapped_input.end()); } { // Validate empty range Read wrapped_empty{span{}}; - auto iter_result = find_last_if_not(wrapped_empty.begin(), wrapped_empty.end(), equals(0), get_first); - auto range_result = find_last_if_not(wrapped_empty, equals(0), get_first); + auto iter_result = find_last_if_not(wrapped_empty.begin(), wrapped_empty.end(), not_equals(0), get_first); + auto range_result = find_last_if_not(wrapped_empty, not_equals(0), get_first); assert(iter_result.begin() == wrapped_empty.end()); assert(range_result.begin() == wrapped_empty.end()); } From e349138ab04740bab661002bb15e611e86eb012a Mon Sep 17 00:00:00 2001 From: Daniel Marshall Date: Wed, 14 Dec 2022 08:54:10 +0000 Subject: [PATCH 09/11] CR comments --- stl/inc/algorithm | 135 +++++------------- .../P1223R5_ranges_alg_find_last/test.cpp | 9 +- .../P1223R5_ranges_alg_find_last_if/test.cpp | 11 +- .../test.cpp | 11 +- 4 files changed, 47 insertions(+), 119 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index a0a9ba1fe9..8f4f7706df 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2807,13 +2807,12 @@ namespace ranges { _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Last, const _Ty& _Value, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); + auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); if constexpr (bidirectional_iterator<_It>) { - auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); auto _UResult = _Find_last_unchecked(_STD move(_UFirst), _STD move(_ULast), _Value, _Pass_fn(_Proj)); return _Rewrap_subrange>(_First, _STD move(_UResult)); } else { - auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); auto _ULast = _Unwrap_sent<_It>(_STD move(_Last)); auto _UResult = _Find_last_unchecked(_STD move(_UFirst), _STD move(_ULast), _Value, _Pass_fn(_Proj)); return _Rewrap_subrange>(_First, _STD move(_UResult)); @@ -2858,8 +2857,8 @@ namespace ranges { } return {_STD move(_Result), _STD move(_Last)}; } else { - _It _Result = _First; - bool _Found = false; + auto _Result = _First; + bool _Found = false; for (;; ++_First) { if (_First == _Last) { if (!_Found) { @@ -2880,6 +2879,7 @@ namespace ranges { _EXPORT_STD inline constexpr _Find_last_fn find_last{_Not_quite_object::_Construct_tag{}}; + template class _Find_last_if_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -2889,20 +2889,20 @@ namespace ranges { _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { _Adl_verify_range(_First, _Last); + auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); if constexpr (bidirectional_iterator<_It>) { - auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); - auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); + auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); auto _UResult = _Find_last_if_unchecked(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); return _Rewrap_subrange>(_First, _STD move(_UResult)); } else { - auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); - auto _ULast = _Unwrap_sent<_It>(_STD move(_Last)); + auto _ULast = _Unwrap_sent<_It>(_STD move(_Last)); auto _UResult = _Find_last_if_unchecked(_STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); return _Rewrap_subrange>(_First, _STD move(_UResult)); } } + template , _Pj>> _Pr> _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { @@ -2926,105 +2926,34 @@ namespace ranges { if constexpr (_Bidi_common<_It, _Se>) { for (auto _Result = _Last; _Result != _First;) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *--_Result))) { - return {_STD move(_Result), _STD move(_Last)}; + if constexpr (_If_not) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *--_Result))) { + return {_STD move(_Result), _STD move(_Last)}; + } + } else { + if (_STD invoke(_Pred, _STD invoke(_Proj, *--_Result))) { + return {_STD move(_Result), _STD move(_Last)}; + } } } return {_Last, _Last}; } else if constexpr (same_as<_It, _Se>) { auto _Result = _Last; for (; _First != _Last; ++_First) { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { - _Result = _First; - } - } - return {_STD move(_Result), _STD move(_Last)}; - } else { - _It _Result = _First; - bool _Found = false; - for (;; ++_First) { - if (_First == _Last) { - if (!_Found) { + if constexpr (_If_not) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + _Result = _First; + } + } else { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { _Result = _First; } - break; - } - - if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { - _Result = _First; - _Found = true; - } - } - return {_STD move(_Result), _STD move(_First)}; - } - } - }; - - _EXPORT_STD inline constexpr _Find_last_if_fn find_last_if{_Not_quite_object::_Construct_tag{}}; - - class _Find_last_if_not_fn : private _Not_quite_object { - public: - using _Not_quite_object::_Not_quite_object; - - template _Se, class _Pj = identity, - indirect_unary_predicate> _Pr> - _NODISCARD constexpr subrange<_It> operator()(_It _First, _Se _Last, _Pr _Pred, _Pj _Proj = {}) const { - _Adl_verify_range(_First, _Last); - - if constexpr (bidirectional_iterator<_It>) { - auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); - auto _ULast = _Get_final_iterator_unwrapped<_It>(_UFirst, _STD move(_Last)); - auto _UResult = _Find_last_if_not_unchecked( - _STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); - return _Rewrap_subrange>(_First, _STD move(_UResult)); - } else { - auto _UFirst = _Unwrap_iter<_Se>(_STD move(_First)); - auto _ULast = _Unwrap_sent<_It>(_STD move(_Last)); - auto _UResult = _Find_last_if_not_unchecked( - _STD move(_UFirst), _STD move(_ULast), _Pass_fn(_Pred), _Pass_fn(_Proj)); - return _Rewrap_subrange>(_First, _STD move(_UResult)); - } - } - template , _Pj>> _Pr> - _NODISCARD constexpr borrowed_subrange_t<_Rng> operator()(_Rng&& _Range, _Pr _Pred, _Pj _Proj = {}) const { - if constexpr (bidirectional_range<_Rng>) { - auto _UResult = _Find_last_if_not_unchecked( - _Ubegin(_Range), _Get_final_iterator_unwrapped(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); - return _Rewrap_subrange>(_Range, _STD move(_UResult)); - } else { - auto _UResult = - _Find_last_if_not_unchecked(_Ubegin(_Range), _Uend(_Range), _Pass_fn(_Pred), _Pass_fn(_Proj)); - return _Rewrap_subrange>(_Range, _STD move(_UResult)); - } - } - - private: - template - _NODISCARD static constexpr subrange<_It> _Find_last_if_not_unchecked( - _It _First, _Se _Last, _Pr _Pred, _Pj _Proj) { - _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It>); - _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); - _STL_INTERNAL_STATIC_ASSERT(indirect_unary_predicate<_Pr, projected<_It, _Pj>>); - - if constexpr (_Bidi_common<_It, _Se>) { - for (auto _Result = _Last; _Result != _First;) { - if (!_STD invoke(_Pred, _STD invoke(_Proj, *--_Result))) { - return {_STD move(_Result), _STD move(_Last)}; - } - } - return {_Last, _Last}; - } else if constexpr (same_as<_It, _Se>) { - auto _Result = _Last; - for (; _First != _Last; ++_First) { - if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { - _Result = _First; } } return {_STD move(_Result), _STD move(_Last)}; } else { - _It _Result = _First; - bool _Found = false; + auto _Result = _First; + bool _Found = false; for (;; ++_First) { if (_First == _Last) { if (!_Found) { @@ -3033,9 +2962,16 @@ namespace ranges { break; } - if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { - _Result = _First; - _Found = true; + if constexpr (_If_not) { + if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + _Result = _First; + _Found = true; + } + } else { + if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { + _Result = _First; + _Found = true; + } } } return {_STD move(_Result), _STD move(_First)}; @@ -3043,7 +2979,8 @@ namespace ranges { } }; - _EXPORT_STD inline constexpr _Find_last_if_not_fn find_last_if_not{_Not_quite_object::_Construct_tag{}}; + _EXPORT_STD inline constexpr _Find_last_if_fn find_last_if{_Not_quite_object::_Construct_tag{}}; + _EXPORT_STD inline constexpr _Find_last_if_fn find_last_if_not{_Not_quite_object::_Construct_tag{}}; #endif // _HAS_CXX23 } // namespace ranges #endif // __cpp_lib_concepts diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp index 754de9b10d..eed280b658 100644 --- a/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp +++ b/tests/std/tests/P1223R5_ranges_alg_find_last/test.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -38,15 +37,13 @@ struct instantiator { { // Validate range overload [found case] Read wrapped_input{haystack}; const auto result = find_last(wrapped_input, value, get_first); - STATIC_ASSERT(same_as, iterator_t>); - STATIC_ASSERT(common_range); + STATIC_ASSERT(same_as>>); check_value(result.front(), value); } { // Validate iterator + sentinel overload [found case] Read wrapped_input{haystack}; - auto result = find_last(wrapped_input.begin(), wrapped_input.end(), value, get_first); - STATIC_ASSERT(same_as, iterator_t>); - STATIC_ASSERT(common_range); + const auto result = find_last(wrapped_input.begin(), wrapped_input.end(), value, get_first); + STATIC_ASSERT(same_as>>); check_value(result.front(), value); } } diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp index 8137bc6c17..c47ded5425 100644 --- a/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if/test.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -40,16 +39,14 @@ struct instantiator { for (const auto& [value, _] : haystack) { { // Validate range overload [found case] Read wrapped_input{haystack}; - auto result = find_last_if(wrapped_input, equals(value), get_first); - STATIC_ASSERT(same_as, iterator_t>); - STATIC_ASSERT(common_range); + const auto result = find_last_if(wrapped_input, equals(value), get_first); + STATIC_ASSERT(same_as>>); check_value(result.front(), value); } { // Validate iterator + sentinel overload [found case] Read wrapped_input{haystack}; - auto result = find_last_if(wrapped_input.begin(), wrapped_input.end(), equals(value), get_first); - STATIC_ASSERT(same_as, iterator_t>); - STATIC_ASSERT(common_range); + const auto result = find_last_if(wrapped_input.begin(), wrapped_input.end(), equals(value), get_first); + STATIC_ASSERT(same_as>>); check_value(result.front(), value); } } diff --git a/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp index adb33ee591..1a6a8465d9 100644 --- a/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp +++ b/tests/std/tests/P1223R5_ranges_alg_find_last_if_not/test.cpp @@ -3,7 +3,6 @@ #include #include -#include #include #include #include @@ -40,17 +39,15 @@ struct instantiator { for (const auto& [value, _] : haystack) { { // Validate range overload [found case] Read wrapped_input{haystack}; - auto result = find_last_if_not(wrapped_input, not_equals(value), get_first); - STATIC_ASSERT(same_as, iterator_t>); - STATIC_ASSERT(common_range); + const auto result = find_last_if_not(wrapped_input, not_equals(value), get_first); + STATIC_ASSERT(same_as>>); check_value(result.front(), value); } { // Validate iterator + sentinel overload [found case] Read wrapped_input{haystack}; - auto result = + const auto result = find_last_if_not(wrapped_input.begin(), wrapped_input.end(), not_equals(value), get_first); - STATIC_ASSERT(same_as, iterator_t>); - STATIC_ASSERT(common_range); + STATIC_ASSERT(same_as>>); check_value(result.front(), value); } } From 2d59ba0a74a429f5438bd0eee270913438080d75 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 14 Dec 2022 07:06:41 -0800 Subject: [PATCH 10/11] simplify branching to remove code duplication --- stl/inc/algorithm | 36 ++++++++++-------------------------- 1 file changed, 10 insertions(+), 26 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 8f4f7706df..16b58e2853 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2926,28 +2926,18 @@ namespace ranges { if constexpr (_Bidi_common<_It, _Se>) { for (auto _Result = _Last; _Result != _First;) { - if constexpr (_If_not) { - if (!_STD invoke(_Pred, _STD invoke(_Proj, *--_Result))) { - return {_STD move(_Result), _STD move(_Last)}; - } - } else { - if (_STD invoke(_Pred, _STD invoke(_Proj, *--_Result))) { - return {_STD move(_Result), _STD move(_Last)}; - } + const bool _Pred_result = _STD invoke(_Pred, _STD invoke(_Proj, *--_Result)); + if (_Pred_result != _If_not) { + return {_STD move(_Result), _STD move(_Last)}; } } return {_Last, _Last}; } else if constexpr (same_as<_It, _Se>) { auto _Result = _Last; for (; _First != _Last; ++_First) { - if constexpr (_If_not) { - if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { - _Result = _First; - } - } else { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { - _Result = _First; - } + const bool _Pred_result = _STD invoke(_Pred, _STD invoke(_Proj, *_First)); + if (_Pred_result != _If_not) { + _Result = _First; } } return {_STD move(_Result), _STD move(_Last)}; @@ -2962,16 +2952,10 @@ namespace ranges { break; } - if constexpr (_If_not) { - if (!_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { - _Result = _First; - _Found = true; - } - } else { - if (_STD invoke(_Pred, _STD invoke(_Proj, *_First))) { - _Result = _First; - _Found = true; - } + const bool _Pred_result = _STD invoke(_Pred, _STD invoke(_Proj, *_First)); + if (_Pred_result != _If_not) { + _Result = _First; + _Found = true; } } return {_STD move(_Result), _STD move(_First)}; From ec18ebeb9efa10ddd3167ded8b3fdee37d51cc51 Mon Sep 17 00:00:00 2001 From: Nicole Mazzuca Date: Wed, 14 Dec 2022 09:26:15 -0800 Subject: [PATCH 11/11] switch to `_Search_for`, use `invoke_r` --- stl/inc/algorithm | 15 ++++++--------- stl/inc/functional | 13 ------------- stl/inc/xutility | 13 +++++++++++++ 3 files changed, 19 insertions(+), 22 deletions(-) diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 16b58e2853..25729d359b 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -2879,7 +2879,7 @@ namespace ranges { _EXPORT_STD inline constexpr _Find_last_fn find_last{_Not_quite_object::_Construct_tag{}}; - template + template class _Find_last_if_fn : private _Not_quite_object { public: using _Not_quite_object::_Not_quite_object; @@ -2926,8 +2926,7 @@ namespace ranges { if constexpr (_Bidi_common<_It, _Se>) { for (auto _Result = _Last; _Result != _First;) { - const bool _Pred_result = _STD invoke(_Pred, _STD invoke(_Proj, *--_Result)); - if (_Pred_result != _If_not) { + if (_STD invoke_r(_Pred, _STD invoke(_Proj, *--_Result)) == _Search_for) { return {_STD move(_Result), _STD move(_Last)}; } } @@ -2935,8 +2934,7 @@ namespace ranges { } else if constexpr (same_as<_It, _Se>) { auto _Result = _Last; for (; _First != _Last; ++_First) { - const bool _Pred_result = _STD invoke(_Pred, _STD invoke(_Proj, *_First)); - if (_Pred_result != _If_not) { + if (_STD invoke_r(_Pred, _STD invoke(_Proj, *_First)) == _Search_for) { _Result = _First; } } @@ -2952,8 +2950,7 @@ namespace ranges { break; } - const bool _Pred_result = _STD invoke(_Pred, _STD invoke(_Proj, *_First)); - if (_Pred_result != _If_not) { + if (_STD invoke_r(_Pred, _STD invoke(_Proj, *_First)) == _Search_for) { _Result = _First; _Found = true; } @@ -2963,8 +2960,8 @@ namespace ranges { } }; - _EXPORT_STD inline constexpr _Find_last_if_fn find_last_if{_Not_quite_object::_Construct_tag{}}; - _EXPORT_STD inline constexpr _Find_last_if_fn find_last_if_not{_Not_quite_object::_Construct_tag{}}; + _EXPORT_STD inline constexpr _Find_last_if_fn find_last_if{_Not_quite_object::_Construct_tag{}}; + _EXPORT_STD inline constexpr _Find_last_if_fn find_last_if_not{_Not_quite_object::_Construct_tag{}}; #endif // _HAS_CXX23 } // namespace ranges #endif // __cpp_lib_concepts diff --git a/stl/inc/functional b/stl/inc/functional index e68769a91f..23735d3505 100644 --- a/stl/inc/functional +++ b/stl/inc/functional @@ -31,19 +31,6 @@ _STL_DISABLE_CLANG_WARNINGS #undef new _STD_BEGIN -#if _HAS_CXX23 -_EXPORT_STD template , int> = 0> -_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) noexcept( - is_nothrow_invocable_r_v<_Result_type, _Callable, _Types...>) { - if constexpr (is_void_v<_Result_type>) { - (void) _STD invoke(static_cast<_Callable&&>(_Obj), static_cast<_Types&&>(_Args)...); - } else { - return _STD invoke(static_cast<_Callable&&>(_Obj), static_cast<_Types&&>(_Args)...); - } -} -#endif // _HAS_CXX23 - // plus, minus, and multiplies are defined in _EXPORT_STD template diff --git a/stl/inc/xutility b/stl/inc/xutility index b8a210a37a..b9402ee837 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -385,6 +385,19 @@ constexpr _Ref_fn<_Fn> _Pass_fn(_Fn& _Val) { // pass functor by "reference" return {_Val}; } +#if _HAS_CXX23 +_EXPORT_STD template , int> = 0> +_NODISCARD constexpr _Result_type invoke_r(_Callable&& _Obj, _Types&&... _Args) noexcept( + is_nothrow_invocable_r_v<_Result_type, _Callable, _Types...>) { + if constexpr (is_void_v<_Result_type>) { + (void) _STD invoke(static_cast<_Callable&&>(_Obj), static_cast<_Types&&>(_Args)...); + } else { + return _STD invoke(static_cast<_Callable&&>(_Obj), static_cast<_Types&&>(_Args)...); + } +} +#endif // _HAS_CXX23 + struct _Unused_parameter { // generic unused parameter struct constexpr _Unused_parameter() noexcept = default; template