diff --git a/stl/inc/algorithm b/stl/inc/algorithm index a8bd4ef18fb..049e11eb817 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -206,6 +206,23 @@ namespace ranges { } }; + // FUNCTION TEMPLATE _Rewrap_subrange + template + _NODISCARD constexpr _Result _Rewrap_subrange( + [[maybe_unused]] _Wrapped& _First, [[maybe_unused]] subrange<_Unwrapped>&& _UResult) { + // conditionally computes a wrapped subrange from a wrapped iterator and unwrapped subrange + _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Unwrapped_t<_Wrapped>, _Unwrapped>); + if constexpr (is_same_v<_Result, dangling>) { + return dangling{}; + } else { + _STL_INTERNAL_STATIC_ASSERT(is_same_v<_Result, subrange<_Wrapped>>); + auto _Last = _First; + _Seek_wrapped(_First, _UResult.begin()); + _Seek_wrapped(_Last, _UResult.end()); + return _Result{_STD move(_First), _STD move(_Last)}; + } + } + #ifdef __clang__ #pragma clang diagnostic pop #endif // __clang__ @@ -1878,6 +1895,158 @@ _NODISCARD _CONSTEXPR20 _FwdItHaystack search( return _Search(_First, _Last).first; } +#ifdef __cpp_lib_concepts +namespace ranges { + // FUNCTION TEMPLATE _Equal_rev_pred + // clang-format off + template + concept _Equal_rev_pred_can_memcmp = is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity> + && is_same_v<_Se2, _It2> && _Equal_memcmp_is_safe<_It1, _It2, _Pr>; + + template _Se2, class _Pr, class _Pj1, class _Pj2> + requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr pair _Equal_rev_pred( + _It1 _First1, _It2 _First2, const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + // Returns {true, _First1 + (_Last2 - _First2)} if [_First1, ...) equals [_First2, _Last2), and {false, {}} + // otherwise. + constexpr bool _Optimize = _Equal_rev_pred_can_memcmp<_It1, _It2, _Se2, _Pr, _Pj1, _Pj2>; + if constexpr (_Optimize) { + if (!_STD is_constant_evaluated()) { + const auto _First1_ch = reinterpret_cast(_STD to_address(_First1)); + const auto _First2_ch = reinterpret_cast(_STD to_address(_First2)); + const auto _Count = + static_cast(reinterpret_cast(_STD to_address(_Last2)) - _First2_ch); + const bool _Eq = _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; + if (_Eq) { + _First1 += (_Last2 - _First2); + return {true, _STD move(_First1)}; + } else { + return {false, _It1{}}; + } + } + } + + for (; _First2 != _Last2; ++_First1, (void) ++_First2) { + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { + return {false, _It1{}}; + } + } + + return {true, _STD move(_First1)}; + } + // clang-format on + + // VARIABLE ranges::search + class _Search_fn : private _Not_quite_object { + public: + using _Not_quite_object::_Not_quite_object; + + // clang-format off + template _Se1, forward_iterator _It2, sentinel_for<_It2> _Se2, + class _Pr = ranges::equal_to, class _Pj1 = identity, class _Pj2 = identity> + requires indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr subrange<_It1> operator()(_It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, + _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + _Adl_verify_range(_First1, _Last1); + _Adl_verify_range(_First2, _Last2); + auto _UFirst1 = _Get_unwrapped(_STD move(_First1)); + auto _ULast1 = _Get_unwrapped(_STD move(_Last1)); + auto _UFirst2 = _Get_unwrapped(_STD move(_First2)); + auto _ULast2 = _Get_unwrapped(_STD move(_Last2)); + + if constexpr (sized_sentinel_for<_Se1, _It1> && sized_sentinel_for<_Se2, _It2>) { + const auto _Count1 = _ULast1 - _UFirst1; + const auto _Count2 = _ULast2 - _UFirst2; + auto _UResult = + _Search_sized(_STD move(_UFirst1), _STD move(_ULast1), _Count1, _STD move(_UFirst2), + _STD move(_ULast2), _Count2, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); + } else { + auto _UResult = _Search_unsized(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), + _STD move(_ULast2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); + } + } + + template + requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> + _NODISCARD constexpr borrowed_subrange_t<_Rng1> operator()( + _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + auto _First1 = _RANGES begin(_Range1); + + if constexpr (sized_range<_Rng1> && sized_range<_Rng2>) { + const auto _Count1 = _RANGES distance(_Range1); + const auto _Count2 = _RANGES distance(_Range2); + auto _UResult = _Search_sized(_Get_unwrapped(_First1), _Uend(_Range1), _Count1, + _Ubegin(_Range2), _Uend(_Range2), _Count2, + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); + } else { + auto _UResult = _Search_unsized(_Get_unwrapped(_First1), _Uend(_Range1), + _Ubegin(_Range2), _Uend(_Range2), + _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); + } + } + // clang-format on + + private: + template + _NODISCARD static constexpr subrange<_It1> _Search_sized(_It1 _First1, const _Se1 _Last1, + iter_difference_t<_It1> _Count1, _It2 _First2, const _Se2 _Last2, const iter_difference_t<_It2> _Count2, + _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + _STL_INTERNAL_CHECK(_RANGES distance(_First1, _Last1) == _Count1); + _STL_INTERNAL_CHECK(_RANGES distance(_First2, _Last2) == _Count2); + + for (; _Count1 >= _Count2; ++_First1, (void) --_Count1) { + auto [_Match, _Mid1] = _RANGES _Equal_rev_pred(_First1, _First2, _Last2, _Pred, _Proj1, _Proj2); + if (_Match) { + return {_STD move(_First1), _STD move(_Mid1)}; + } + } + + _First1 = _Find_last_iterator(_First1, _Last1, _Count1); + return {_First1, _First1}; + } + + template + _NODISCARD static constexpr subrange<_It1> _Search_unsized( + _It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + + for (;; ++_First1) { + auto _Mid1 = _First1; + for (auto _Mid2 = _First2;; ++_Mid1, (void) ++_Mid2) { + if (_Mid2 == _Last2) { // match + return {_STD move(_First1), _STD move(_Mid1)}; + } + + if (_Mid1 == _Last1) { // not enough haystack left to find a match + return {_Mid1, _Mid1}; + } + + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Mid1), _STD invoke(_Proj2, *_Mid2))) { // mismatch + break; + } + } + } + } + }; + + inline constexpr _Search_fn search{_Not_quite_object::_Construct_tag{}}; +} // namespace ranges +#endif // __cpp_lib_concepts + // FUNCTION TEMPLATE search_n #if _HAS_IF_CONSTEXPR template @@ -2251,47 +2420,27 @@ namespace ranges { // VARIABLE ranges::find_end class _Find_end_fn : private _Not_quite_object { private: - template - _NODISCARD static constexpr bool _Equal_rev_pred_unchecked( - _It1 _First1, _It2 _First2, const _It2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { - // compare [_First1, ...) to [_First2, _Last2) - constexpr bool _Both_identity = is_same_v<_Pj1, identity> && is_same_v<_Pj2, identity>; - // Performance note: teach _Equal_memcmp_is_safe about contiguous iterators and ranges::equal_to - if constexpr (_Both_identity && _Equal_memcmp_is_safe<_It1, _It2, _Pr>) { - if (!_STD is_constant_evaluated()) { - const auto _First1_ch = reinterpret_cast(_STD to_address(_First1)); - const auto _First2_ch = reinterpret_cast(_STD to_address(_First2)); - const auto _Count = - static_cast(reinterpret_cast(_STD to_address(_Last2)) - _First2_ch); - return _CSTD memcmp(_First1_ch, _First2_ch, _Count) == 0; - } - } - - for (; _First2 != _Last2; ++_First1, (void) ++_First2) { - if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_First1), _STD invoke(_Proj2, *_First2))) { - return false; - } - } - - return true; - } - template _NODISCARD static constexpr subrange<_It1> _Random_access_sized_ranges(_It1 _First1, const iter_difference_t<_It1> _Count1, _It2 _First2, const iter_difference_t<_It2> _Count2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(random_access_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + // pre: _First1 + [0, _Count1) is a valid counted range + // pre: _First2 + [0, _Count2) is a valid counted range + if (_Count2 > 0 && _Count2 <= _Count1) { - const auto _UFirst1 = _Get_unwrapped(_First1); - const auto _UFirst2 = _Get_unwrapped(_First2); const auto _Count2_as1 = static_cast>(_Count2); - for (auto _UCandidate = _UFirst1 + (_Count1 - _Count2_as1);; --_UCandidate) { - if (_Equal_rev_pred_unchecked(_UCandidate, _UFirst2, _UFirst2 + _Count2, _Pred, _Proj1, _Proj2)) { - _Seek_wrapped(_First1, _UCandidate); - return {_First1, _First1 + _Count2_as1}; + for (auto _Candidate = _First1 + (_Count1 - _Count2_as1);; --_Candidate) { + auto [_Match, _Mid1] = + _Equal_rev_pred(_Candidate, _First2, _First2 + _Count2, _Pred, _Proj1, _Proj2); + if (_Match) { + return {_STD move(_Candidate), _STD move(_Mid1)}; } - if (_UCandidate == _UFirst1) { + if (_Candidate == _First1) { break; } } @@ -2303,29 +2452,27 @@ namespace ranges { template _NODISCARD static constexpr subrange<_It1> _Bidi_common_ranges( - _It1 _First1, _It1 _Last1, _It2 _First2, _It2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { - auto _UFirst1 = _Get_unwrapped(_First1); - const auto _ULast1 = _Get_unwrapped(_Last1); - const auto _UFirst2 = _Get_unwrapped(_First2); - const auto _ULast2 = _Get_unwrapped(_Last2); - for (auto _UCandidate = _ULast1;; --_UCandidate) { // try a match at _UCandidate - auto _UNext1 = _UCandidate; - auto _UNext2 = _ULast2; - for (;;) { // test if [_UFirst2, _ULast2) is a suffix of [_UFirst1, _UCandidate) - if (_UFirst2 == _UNext2) { // match found - _Seek_wrapped(_First1, _UNext1); - _Seek_wrapped(_Last1, _UCandidate); - return {_First1, _Last1}; + _It1 _First1, _It1 _Last1, _It2 _First2, const _It2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(bidirectional_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + + for (auto _Candidate = _Last1;; --_Candidate) { // try a match at _Candidate + auto _Next1 = _Candidate; + auto _Next2 = _Last2; + for (;;) { // test if [_First2, _Last2) is a suffix of [_First1, _Candidate) + if (_First2 == _Next2) { // match found + return {_STD move(_First1), _STD move(_Last1)}; } - if (_UFirst1 == _UNext1) { - // [_UFirst1, _UCandidate) is shorter than [_UFirst2, _ULast2), remaining candidates nonviable + if (_First1 == _Next1) { + // [_First1, _Candidate) is shorter than [_First2, _Last2); remaining candidates nonviable return {_Last1, _Last1}; } - --_UNext1; - --_UNext2; - if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_UNext1), _STD invoke(_Proj2, *_UNext2))) { + --_Next1; + --_Next2; + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Next1), _STD invoke(_Proj2, *_Next2))) { break; // mismatch } } @@ -2334,49 +2481,42 @@ namespace ranges { template _NODISCARD static constexpr subrange<_It1> _Forward_ranges( - _It1 _First1, _Se1 _Last1, _It2 _First2, _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { - auto _UFirst1 = _Get_unwrapped(_First1); - const auto _ULast1 = _Get_unwrapped(_Last1); - const auto _UFirst2 = _Get_unwrapped(_First2); - const auto _ULast2 = _Get_unwrapped(_Last2); - decltype(_UFirst1) _UMatch_first; - decltype(_UFirst1) _UMatch_last; + _It1 _First1, const _Se1 _Last1, _It2 _First2, const _Se2 _Last2, _Pr _Pred, _Pj1 _Proj1, _Pj2 _Proj2) { + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It1>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se1, _It1>); + _STL_INTERNAL_STATIC_ASSERT(forward_iterator<_It2>); + _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se2, _It2>); + _STL_INTERNAL_STATIC_ASSERT(indirectly_comparable<_It1, _It2, _Pr, _Pj1, _Pj2>); + + subrange<_It1> _Match{}; bool _Found = false; - for (;;) { // try a match at _UFirst1 - auto _UNext1 = _UFirst1; - auto _UNext2 = _UFirst2; - for (;;) { // test if [_UFirst2, _ULast2) is a prefix of [_UFirst1, _ULast1) - const bool _End_of_needle = _UNext2 == _ULast2; + for (;; ++_First1) { // try a match at _First1 + auto _Next1 = _First1; + auto _Next2 = _First2; + for (;; ++_Next1, (void) ++_Next2) { // test if [_First2, _Last2) is a prefix of [_First1, _Last1) + const bool _End_of_needle = _Next2 == _Last2; if (_End_of_needle) { // match candidate found - _UMatch_first = _UFirst1; - _UMatch_last = _UNext1; - _Found = true; + _Match = subrange{_First1, _Next1}; + _Found = true; } - if (_UNext1 == _ULast1) { // haystack exhausted - _Seek_wrapped(_First1, _Found ? _UMatch_first : _UFirst1); - auto _Mid1 = _First1; - if (_Found) { - _Seek_wrapped(_Mid1, _UMatch_last); + if (_Next1 == _Last1) { // haystack exhausted + if (!_Found) { + _Match = subrange{_Next1, _Next1}; } - return {_First1, _Mid1}; + return _Match; } if (_End_of_needle) { break; // end of match found, go to the next candidate } - if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_UNext1), _STD invoke(_Proj2, *_UNext2))) { + if (!_STD invoke(_Pred, _STD invoke(_Proj1, *_Next1), _STD invoke(_Proj2, *_Next2))) { break; // mismatch, go to the next candidate } - - ++_UNext1; - ++_UNext2; } - - ++_UFirst1; } } @@ -2391,16 +2531,26 @@ namespace ranges { _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { _Adl_verify_range(_First1, _Last1); _Adl_verify_range(_First2, _Last2); + auto _UFirst1 = _Get_unwrapped(_First1); + auto _ULast1 = _Get_unwrapped(_Last1); + auto _UFirst2 = _Get_unwrapped(_First2); + auto _ULast2 = _Get_unwrapped(_Last2); + if constexpr (random_access_iterator<_It1> && sized_sentinel_for<_Se1, _It1> && random_access_iterator<_It2> && sized_sentinel_for<_Se2, _It2>) { - return _Random_access_sized_ranges(_First1, _Last1 - _First1, _First2, _Last2 - _First2, + const auto _Count1 = _ULast1 - _UFirst1; + const auto _Count2 = _ULast2 - _UFirst2; + auto _UResult = _Random_access_sized_ranges(_STD move(_UFirst1), _Count1, _STD move(_UFirst2), _Count2, _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else if constexpr (_Bidi_common<_It1, _Se1> && _Bidi_common<_It2, _Se2>) { - return _Bidi_common_ranges(_First1, _Last1, _First2, _Last2, - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _UResult = _Bidi_common_ranges(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), + _STD move(_ULast2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else { - return _Forward_ranges(_First1, _Last1, _First2, _Last2, - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _UResult = _Forward_ranges(_STD move(_UFirst1), _STD move(_ULast1), _STD move(_UFirst2), + _STD move(_ULast2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); } } @@ -2409,22 +2559,20 @@ namespace ranges { requires indirectly_comparable, iterator_t<_Rng2>, _Pr, _Pj1, _Pj2> _NODISCARD constexpr borrowed_subrange_t<_Rng1> operator()( _Rng1&& _Range1, _Rng2&& _Range2, _Pr _Pred = {}, _Pj1 _Proj1 = {}, _Pj2 _Proj2 = {}) const { + auto _First1 = _RANGES begin(_Range1); if constexpr (random_access_range<_Rng1> && sized_range<_Rng1> && random_access_range<_Rng2> && sized_range<_Rng2>) { - return _Random_access_sized_ranges( - _RANGES begin(_Range1), _RANGES distance(_Range1), - _RANGES begin(_Range2), _RANGES distance(_Range2), - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _UResult = _Random_access_sized_ranges(_Get_unwrapped(_First1), _RANGES distance(_Range1), + _Ubegin(_Range2), _RANGES distance(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else if constexpr (_Bidi_common_range<_Rng1> && _Bidi_common_range<_Rng2>) { - return _Bidi_common_ranges( - _RANGES begin(_Range1), _RANGES end(_Range1), - _RANGES begin(_Range2), _RANGES end(_Range2), - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _UResult = _Bidi_common_ranges(_Get_unwrapped(_First1), _Uend(_Range1), + _Ubegin(_Range2), _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); } else { - return _Forward_ranges( - _RANGES begin(_Range1), _RANGES end(_Range1), - _RANGES begin(_Range2), _RANGES end(_Range2), - _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + auto _UResult = _Forward_ranges(_Get_unwrapped(_First1), _Uend(_Range1), + _Ubegin(_Range2), _Uend(_Range2), _Pass_fn(_Pred), _Pass_fn(_Proj1), _Pass_fn(_Proj2)); + return _Rewrap_subrange>(_First1, _STD move(_UResult)); } } // clang-format on diff --git a/stl/inc/xutility b/stl/inc/xutility index c4f918984bd..45f60c53788 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -1671,7 +1671,7 @@ _CONSTEXPR17 void advance(_InIt& _Where, _Diff _Off) { // increment iterator by _STL_ASSERT(_Off >= 0, "negative advance of non-bidirectional iterator"); } - auto&& _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); + decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); constexpr bool _Need_rewrap = !is_reference_v; if constexpr (is_signed_v<_Diff> && _Is_bidi_iter_v<_InIt>) { @@ -1695,7 +1695,7 @@ _CONSTEXPR17 void _Advance1(_InIt& _Where, _Diff _Off, input_iterator_tag) { // increment iterator by offset, input iterators _STL_ASSERT(_Off >= 0, "negative advance of non-bidirectional iterator"); - auto&& _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); + decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); constexpr bool _Need_rewrap = !is_reference_v; for (; 0 < _Off; --_Off) { @@ -1710,7 +1710,7 @@ _CONSTEXPR17 void _Advance1(_InIt& _Where, _Diff _Off, input_iterator_tag) { template _CONSTEXPR17 void _Advance1(_BidIt& _Where, _Diff _Off, bidirectional_iterator_tag) { // increment iterator by offset, bidirectional iterators - auto&& _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); + decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); constexpr bool _Need_rewrap = !is_reference_v; for (; 0 < _Off; --_Off) { @@ -3111,7 +3111,7 @@ namespace ranges { _STL_ASSERT(_Off >= 0, "negative advance of non-bidirectional iterator"); } - auto&& _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); + decltype(auto) _UWhere = _Get_unwrapped_n(_STD move(_Where), _Off); constexpr bool _Need_rewrap = !is_reference_v; if constexpr (bidirectional_iterator<_It>) { @@ -3139,9 +3139,9 @@ namespace ranges { } else { _Adl_verify_range(_Where, _Last); - auto&& _UWhere = _Get_unwrapped(static_cast<_It&&>(_Where)); + decltype(auto) _UWhere = _Get_unwrapped(static_cast<_It&&>(_Where)); constexpr bool _Need_rewrap = !is_reference_v(_Where)))>; - auto&& _ULast = _Get_unwrapped(static_cast<_Se&&>(_Last)); + decltype(auto) _ULast = _Get_unwrapped(static_cast<_Se&&>(_Last)); while (_UWhere != _ULast) { ++_UWhere; diff --git a/tests/std/test.lst b/tests/std/test.lst index 07a87cb3177..13226d7f9c4 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -253,6 +253,7 @@ tests\P0896R4_ranges_alg_is_permutation tests\P0896R4_ranges_alg_mismatch tests\P0896R4_ranges_alg_move tests\P0896R4_ranges_alg_none_of +tests\P0896R4_ranges_alg_search tests\P0896R4_ranges_iterator_machinery tests\P0896R4_ranges_range_machinery tests\P0896R4_ranges_subrange diff --git a/tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp b/tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp index b0d2b1ff0ac..a5e9b2716da 100644 --- a/tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_find_end/test.cpp @@ -6,6 +6,7 @@ #include #include #include +#include #include // #include @@ -58,6 +59,16 @@ constexpr void smoke_test() { assert(result.begin() == pairs.end()); assert(result.end() == pairs.end()); } + + { + // Validate the memcmp optimization + const int haystack[] = {1, 2, 3, 1, 2, 3, 1, 2, 3}; + const int needle[] = {1, 2, 3}; + const auto result = find_end(haystack, std::span{needle}); + STATIC_ASSERT(same_as>); + assert(result.begin() == haystack + 6); + assert(result.end() == haystack + 9); + } } int main() { diff --git a/tests/std/tests/P0896R4_ranges_alg_search/env.lst b/tests/std/tests/P0896R4_ranges_alg_search/env.lst new file mode 100644 index 00000000000..f3ccc8613c6 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_search/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_search/test.cpp b/tests/std/tests/P0896R4_ranges_alg_search/test.cpp new file mode 100644 index 00000000000..cc284d34379 --- /dev/null +++ b/tests/std/tests/P0896R4_ranges_alg_search/test.cpp @@ -0,0 +1,175 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +// +#include + +using namespace std; + +using P = pair; + +// Validate dangling story +STATIC_ASSERT(same_as{}, borrowed{})), ranges::dangling>); +STATIC_ASSERT(same_as{}, borrowed{})), ranges::subrange>); + +struct instantiator { + static constexpr array pairs = { + P{0, 42}, P{1, 42}, P{2, 42}, P{3, 42}, P{4, 42}, P{5, 42}, P{6, 42}, P{7, 42}}; + static constexpr array not_pairs = {2, 3, 4}; + static constexpr array neg_not_pairs = {-2, -3, -4}; + + template + static constexpr void call() { + const Fwd1 range1{pairs}; + { + const Fwd2 range2{not_pairs}; + + // defaulted predicate and projections + { + auto result = ranges::search(range1, range1); + STATIC_ASSERT(same_as>>); + assert(result.begin() == range1.begin()); + assert(result.end() == range1.end()); + } + { + auto result = ranges::search( + ranges::begin(range2), ranges::end(range2), ranges::begin(range2), ranges::end(range2)); + STATIC_ASSERT(same_as>>); + assert(result.begin() == range2.begin()); + assert(result.end() == range2.end()); + } + } + + const Fwd2 range2{neg_not_pairs}; + const auto pred = [](int x, int y) { return x == -y; }; + + // explicit predicate + { + auto result = ranges::search(range2, range2, ranges::equal_to{}); + STATIC_ASSERT(same_as>>); + assert(result.begin() == range2.begin()); + assert(result.end() == range2.end()); + } + { + auto result = ranges::search(ranges::begin(range1), ranges::end(range1), ranges::begin(range1), + ranges::end(range1), ranges::equal_to{}); + STATIC_ASSERT(same_as>>); + assert(result.begin() == range1.begin()); + assert(result.end() == range1.end()); + } + + // explicit predicate and one projection + { + auto result = ranges::search(range1, range2, pred, get_first); + STATIC_ASSERT(same_as>>); + assert(result.begin() == ranges::next(range1.begin(), 2)); + assert(result.end() == ranges::next(range1.begin(), 5)); + } + { + auto result = ranges::search(ranges::begin(range1), ranges::end(range1), ranges::begin(range2), + ranges::end(range2), pred, get_first); + STATIC_ASSERT(same_as>>); + assert(result.begin() == ranges::next(range1.begin(), 2)); + assert(result.end() == ranges::next(range1.begin(), 5)); + } + + // explicit predicate and two projections + constexpr auto minus1 = [](int x) { return x - 1; }; + { + auto result = ranges::search(range1, range2, pred, get_first, minus1); + STATIC_ASSERT(same_as>>); + assert(result.begin() == ranges::next(range1.begin(), 3)); + assert(result.end() == ranges::next(range1.begin(), 6)); + } + { + auto result = ranges::search(ranges::begin(range1), ranges::end(range1), ranges::begin(range2), + ranges::end(range2), pred, get_first, minus1); + STATIC_ASSERT(same_as>>); + assert(result.begin() == ranges::next(range1.begin(), 3)); + assert(result.end() == ranges::next(range1.begin(), 6)); + } + + // negative case + { + auto result = ranges::search(range2, range1, pred, minus1, get_first); + STATIC_ASSERT(same_as>>); + assert(result.empty()); + } + { + auto result = ranges::search(ranges::begin(range2), ranges::end(range2), ranges::begin(range1), + ranges::end(range1), pred, minus1, get_first); + STATIC_ASSERT(same_as>>); + assert(result.empty()); + } + } +}; + +using Elem1 = const P; +using Elem2 = const int; + +#ifdef TEST_EVERYTHING +int main() { + // No constexpr test here; the test_fwd_fwd call exceeds the maximum number of steps in a constexpr computation. + test_fwd_fwd(); +} +#else // ^^^ test all range combinations // test only interesting range combos vvv +template +using fwd_test_range = test::range; +template +using random_test_range = test::range; + +constexpr bool run_tests() { + // All (except contiguous) proxy reference types, since the algorithm doesn't really care. Cases with only 1 range + // sized are not interesting; common is interesting only in that it's necessary to trigger memcmp optimization. + + using test::Common, test::Sized; + + // both forward, non-common, and sized or unsized + instantiator::call, fwd_test_range>(); + instantiator::call, fwd_test_range>(); + + // both random-access, and sized or unsized; all permutations of common + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + instantiator::call, + random_test_range>(); + + { + // Validate the memcmp optimization + const int haystack[] = {1, 2, 3, 1, 2, 3, 1, 2, 3}; + const int needle[] = {1, 2, 3}; + const auto result = ranges::search(span{haystack}, needle); + STATIC_ASSERT(same_as::iterator>>); + assert(to_address(result.begin()) == haystack + 0); + assert(to_address(result.end()) == haystack + 3); + } + + return true; +} + +int main() { + STATIC_ASSERT(run_tests()); + run_tests(); +} +#endif // TEST_EVERYTHING