diff --git a/stl/inc/algorithm b/stl/inc/algorithm index 9fcee8193a..da690a21c5 100644 --- a/stl/inc/algorithm +++ b/stl/inc/algorithm @@ -10260,12 +10260,27 @@ namespace ranges { using _Memcmp_classification_pred = typename decltype(_Lex_compare_memcmp_classify(_First1, _First2, _Pred))::_Pred; - if constexpr (!is_void_v<_Memcmp_classification_pred> && sized_sentinel_for<_Se1, _It1> // - && sized_sentinel_for<_Se2, _It2> && same_as<_Pj1, identity> && same_as<_Pj2, identity>) { + constexpr bool _Is_sized1 = sized_sentinel_for<_Se1, _It1>; + constexpr bool _Is_sized2 = sized_sentinel_for<_Se2, _It2>; + if constexpr (!is_void_v<_Memcmp_classification_pred> && _Sized_or_unreachable_sentinel_for<_Se1, _It1> // + && _Sized_or_unreachable_sentinel_for<_Se2, _It2> // + && same_as<_Pj1, identity> && same_as<_Pj2, identity>) { if (!_STD is_constant_evaluated()) { - const auto _Num1 = static_cast(_Last1 - _First1); - const auto _Num2 = static_cast(_Last2 - _First2); - const int _Ans = _Memcmp_count(_First1, _First2, (_STD min)(_Num1, _Num2)); + size_t _Num1; + if constexpr (_Is_sized1) { + _Num1 = static_cast(_Last1 - _First1); + } else { + _Num1 = SIZE_MAX; + } + + size_t _Num2; + if constexpr (_Is_sized2) { + _Num2 = static_cast(_Last2 - _First2); + } else { + _Num2 = SIZE_MAX; + } + + const int _Ans = _Memcmp_count(_First1, _First2, (_STD min)(_Num1, _Num2)); return _Memcmp_classification_pred{}(_Ans, 0) || (_Ans == 0 && _Num1 < _Num2); } } diff --git a/stl/inc/memory b/stl/inc/memory index 9eca0290ce..de4addfbe7 100644 --- a/stl/inc/memory +++ b/stl/inc/memory @@ -78,10 +78,20 @@ namespace ranges { _STL_INTERNAL_STATIC_ASSERT(_No_throw_sentinel_for<_OSe, _Out>); _STL_INTERNAL_STATIC_ASSERT(constructible_from, iter_reference_t<_It>>); - if constexpr (_Ptr_copy_cat<_It, _Out>::_Really_trivial - && sized_sentinel_for<_Se, _It> && sized_sentinel_for<_OSe, _Out>) { - return _Copy_memcpy_common(_IFirst, _RANGES next(_IFirst, _STD move(_ILast)), _OFirst, - _RANGES next(_OFirst, _STD move(_OLast))); + constexpr bool _Is_sized1 = sized_sentinel_for<_Se, _It>; + constexpr bool _Is_sized2 = sized_sentinel_for<_OSe, _Out>; + if constexpr (_Ptr_copy_cat<_It, _Out>::_Really_trivial && _Sized_or_unreachable_sentinel_for<_Se, _It> // + && _Sized_or_unreachable_sentinel_for<_OSe, _Out>) { + if constexpr (_Is_sized1 && _Is_sized2) { + return _Copy_memcpy_common(_IFirst, _RANGES next(_IFirst, _STD move(_ILast)), _OFirst, + _RANGES next(_OFirst, _STD move(_OLast))); + } else if constexpr (_Is_sized1) { + return _Copy_memcpy_distance(_IFirst, _OFirst, _IFirst, _RANGES next(_IFirst, _STD move(_ILast))); + } else if constexpr (_Is_sized2) { + return _Copy_memcpy_distance(_IFirst, _OFirst, _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); + } else { + _STL_ASSERT(false, "Tried to uninitialized_copy two ranges with unreachable sentinels"); + } } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -147,13 +157,20 @@ namespace ranges { } _Adl_verify_range(_First2, _Last2); - auto _IFirst = _Get_unwrapped_n(_STD move(_First1), _Count); - auto _OFirst = _Get_unwrapped(_STD move(_First2)); - const auto _OLast = _Get_unwrapped(_STD move(_Last2)); - if constexpr (_Ptr_copy_cat<_It, _Out>::_Really_trivial) { - auto _UResult = _Copy_memcpy_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); - _IFirst = _UResult.in; - _OFirst = _UResult.out; + auto _IFirst = _Get_unwrapped_n(_STD move(_First1), _Count); + auto _OFirst = _Get_unwrapped(_STD move(_First2)); + auto _OLast = _Get_unwrapped(_STD move(_Last2)); + if constexpr (_Ptr_copy_cat<_It, _Out>::_Really_trivial && _Sized_or_unreachable_sentinel_for<_OSe, _Out>) { + if constexpr (sized_sentinel_for<_OSe, _Out>) { + auto _UResult = _Copy_memcpy_common( + _IFirst, _IFirst + _Count, _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); + _IFirst = _STD move(_UResult.in); + _OFirst = _STD move(_UResult.out); + } else { + auto _UResult = _Copy_memcpy_count(_IFirst, _OFirst, static_cast(_Count)); + _IFirst = _STD move(_UResult.in); + _OFirst = _STD move(_UResult.out); + } } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; @@ -286,10 +303,17 @@ namespace ranges { auto _IFirst = _Get_unwrapped_n(_STD move(_First1), _Count); auto _OFirst = _Get_unwrapped(_STD move(_First2)); const auto _OLast = _Get_unwrapped(_STD move(_Last2)); - if constexpr (_Ptr_move_cat<_It, _Out>::_Really_trivial) { - auto _UResult = _Copy_memcpy_common(_IFirst, _IFirst + _Count, _OFirst, _OLast); - _IFirst = _UResult.in; - _OFirst = _UResult.out; + if constexpr (_Ptr_move_cat<_It, _Out>::_Really_trivial && _Sized_or_unreachable_sentinel_for<_OSe, _Out>) { + if constexpr (sized_sentinel_for<_OSe, _Out>) { + auto _UResult = _Copy_memcpy_common( + _IFirst, _IFirst + _Count, _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); + _IFirst = _STD move(_UResult.in); + _OFirst = _STD move(_UResult.out); + } else { + auto _UResult = _Copy_memcpy_count(_IFirst, _OFirst, static_cast(_Count)); + _IFirst = _STD move(_UResult.in); + _OFirst = _STD move(_UResult.out); + } } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; diff --git a/stl/inc/xmemory b/stl/inc/xmemory index b4bb3212ee..a267c452c1 100644 --- a/stl/inc/xmemory +++ b/stl/inc/xmemory @@ -1533,29 +1533,79 @@ namespace ranges { && _No_throw_forward_iterator>; // clang-format on + template + in_out_result<_InIt, _OutIt> _Copy_memcpy_count(_InIt _IFirst, _OutIt _OFirst, const size_t _Count) noexcept { + const auto _IFirstPtr = _To_address(_IFirst); + const auto _OFirstPtr = _To_address(_OFirst); + const auto _IFirst_ch = const_cast(reinterpret_cast(_IFirstPtr)); + const auto _OFirst_ch = const_cast(reinterpret_cast(_OFirstPtr)); + const size_t _Count_bytes = _Count * sizeof(iter_value_t<_InIt>); + _CSTD memcpy(_OFirst_ch, _IFirst_ch, _Count_bytes); + if constexpr (is_pointer_v<_InIt>) { + _IFirst = reinterpret_cast<_InIt>(_IFirst_ch + _Count_bytes); + } else { + _IFirst += _Count; + } + + if constexpr (is_pointer_v<_OutIt>) { + _OFirst = reinterpret_cast<_OutIt>(_OFirst_ch + _Count_bytes); + } else { + _OFirst += _Count; + } + return {_STD move(_IFirst), _STD move(_OFirst)}; + } + + template + in_out_result<_InIt, _OutIt> _Copy_memcpy_distance( + _InIt _IFirst, _OutIt _OFirst, const _DistIt _DFirst, const _DistIt _DLast) noexcept { + // equivalent to _Copy_memcpy_count(_IFirst, _OFirst, _DLast - _DFirst) but computes distance more efficiently + const auto _IFirstPtr = _To_address(_IFirst); + const auto _OFirstPtr = _To_address(_OFirst); + const auto _DFirstPtr = _To_address(_DFirst); + const auto _DLastPtr = _To_address(_DLast); + const auto _IFirst_ch = const_cast(reinterpret_cast(_IFirstPtr)); + const auto _OFirst_ch = const_cast(reinterpret_cast(_OFirstPtr)); + const auto _DFirst_ch = const_cast(reinterpret_cast(_DFirstPtr)); + const auto _DLast_ch = const_cast(reinterpret_cast(_DLastPtr)); + const auto _Count_bytes = static_cast(_DLast_ch - _DFirst_ch); + _CSTD memcpy(_OFirst_ch, _IFirst_ch, _Count_bytes); + if constexpr (is_pointer_v<_InIt>) { + _IFirst = reinterpret_cast<_InIt>(_IFirst_ch + _Count_bytes); + } else { + _IFirst += _Count_bytes / sizeof(iter_value_t<_InIt>); + } + + if constexpr (is_pointer_v<_OutIt>) { + _OFirst = reinterpret_cast<_OutIt>(_OFirst_ch + _Count_bytes); + } else { + _OFirst += _Count_bytes / sizeof(iter_value_t<_OutIt>); + } + return {_STD move(_IFirst), _STD move(_OFirst)}; + } + template in_out_result<_InIt, _OutIt> _Copy_memcpy_common( _InIt _IFirst, _InIt _ILast, _OutIt _OFirst, _OutIt _OLast) noexcept { - const auto _IFirstPtr = _To_address(_IFirst); - const auto _ILastPtr = _To_address(_ILast); - const auto _OFirstPtr = _To_address(_OFirst); - const auto _OLastPtr = _To_address(_OLast); - const auto _IFirst_ch = const_cast(reinterpret_cast(_IFirstPtr)); - const auto _ILast_ch = const_cast(reinterpret_cast(_ILastPtr)); - const auto _OFirst_ch = const_cast(reinterpret_cast(_OFirstPtr)); - const auto _OLast_ch = const_cast(reinterpret_cast(_OLastPtr)); - const auto _Count = static_cast((_STD min)(_ILast_ch - _IFirst_ch, _OLast_ch - _OFirst_ch)); - _CSTD memcpy(_OFirst_ch, _IFirst_ch, _Count); + const auto _IFirstPtr = _To_address(_IFirst); + const auto _ILastPtr = _To_address(_ILast); + const auto _OFirstPtr = _To_address(_OFirst); + const auto _OLastPtr = _To_address(_OLast); + const auto _IFirst_ch = const_cast(reinterpret_cast(_IFirstPtr)); + const auto _ILast_ch = const_cast(reinterpret_cast(_ILastPtr)); + const auto _OFirst_ch = const_cast(reinterpret_cast(_OFirstPtr)); + const auto _OLast_ch = const_cast(reinterpret_cast(_OLastPtr)); + const auto _Count_bytes = static_cast((_STD min)(_ILast_ch - _IFirst_ch, _OLast_ch - _OFirst_ch)); + _CSTD memcpy(_OFirst_ch, _IFirst_ch, _Count_bytes); if constexpr (is_pointer_v<_InIt>) { - _IFirst = reinterpret_cast<_InIt>(_IFirst_ch + _Count); + _IFirst = reinterpret_cast<_InIt>(_IFirst_ch + _Count_bytes); } else { - _IFirst += _Count / sizeof(iter_value_t<_InIt>); + _IFirst += _Count_bytes / sizeof(iter_value_t<_InIt>); } if constexpr (is_pointer_v<_OutIt>) { - _OFirst = reinterpret_cast<_OutIt>(_OFirst_ch + _Count); + _OFirst = reinterpret_cast<_OutIt>(_OFirst_ch + _Count_bytes); } else { - _OFirst += _Count / sizeof(iter_value_t<_OutIt>); + _OFirst += _Count_bytes / sizeof(iter_value_t<_OutIt>); } return {_STD move(_IFirst), _STD move(_OFirst)}; } @@ -1572,10 +1622,20 @@ namespace ranges { uninitialized_move_result<_It, _Out> _Uninitialized_move_unchecked( _It _IFirst, _Se _ILast, _Out _OFirst, _OSe _OLast) { // clang-format on - if constexpr (_Ptr_move_cat<_It, _Out>::_Really_trivial - && sized_sentinel_for<_Se, _It> && sized_sentinel_for<_OSe, _Out>) { - return _Copy_memcpy_common( - _IFirst, _RANGES next(_IFirst, _STD move(_ILast)), _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); + constexpr bool _Is_sized1 = sized_sentinel_for<_Se, _It>; + constexpr bool _Is_sized2 = sized_sentinel_for<_OSe, _Out>; + if constexpr (_Ptr_move_cat<_It, _Out>::_Really_trivial && _Sized_or_unreachable_sentinel_for<_Se, _It> // + && _Sized_or_unreachable_sentinel_for<_OSe, _Out>) { + if constexpr (_Is_sized1 && _Is_sized2) { + return _Copy_memcpy_common(_IFirst, _RANGES next(_IFirst, _STD move(_ILast)), _OFirst, + _RANGES next(_OFirst, _STD move(_OLast))); + } else if constexpr (_Is_sized1) { + return _Copy_memcpy_distance(_IFirst, _OFirst, _IFirst, _RANGES next(_IFirst, _STD move(_ILast))); + } else if constexpr (_Is_sized2) { + return _Copy_memcpy_distance(_IFirst, _OFirst, _OFirst, _RANGES next(_OFirst, _STD move(_OLast))); + } else { + _STL_ASSERT(false, "Tried to uninitialized_move two ranges with unreachable sentinels"); + } } else { _Uninitialized_backout _Backout{_STD move(_OFirst)}; diff --git a/stl/inc/xutility b/stl/inc/xutility index 9091ec0bfb..d58d4fa96a 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -5362,20 +5362,36 @@ _NODISCARD _FwdIt find(_ExPo&& _Exec, _FwdIt _First, const _FwdIt _Last, const _ namespace ranges { // VARIABLE ranges::find // clang-format off + template + concept _Sized_or_unreachable_sentinel_for = sized_sentinel_for<_Se, _It> || same_as<_Se, unreachable_sentinel_t>; + // concept-constrained for strict enforcement as it is used by several algorithms template _Se, class _Ty, class _Pj = identity> requires indirect_binary_predicate, const _Ty*> _NODISCARD constexpr _It _Find_unchecked(_It _First, const _Se _Last, const _Ty& _Val, _Pj _Proj = {}) { // clang-format on - if constexpr (_Memchr_in_find_is_safe<_It, _Ty> && sized_sentinel_for<_Se, _It> && same_as<_Pj, identity>) { + constexpr bool _Is_sized = sized_sentinel_for<_Se, _It>; + if constexpr (_Memchr_in_find_is_safe<_It, + _Ty> && _Sized_or_unreachable_sentinel_for<_Se, _It> && same_as<_Pj, identity>) { if (!_STD is_constant_evaluated()) { if (!_Within_limits(_First, _Val)) { - return _RANGES next(_STD move(_First), _Last); + if constexpr (_Is_sized) { + return _RANGES next(_STD move(_First), _Last); + } else { + _STL_ASSERT(false, "Tried to find a value in a range with unreachable sentinel" + " that cannot be represented by the range's value type"); + } } + size_t _Count; + if constexpr (_Is_sized) { + _Count = static_cast(_Last - _First); + } else { + _Count = SIZE_MAX; + } const auto _First_ptr = _STD to_address(_First); const auto _Result = static_cast>*>( - _CSTD memchr(_First_ptr, static_cast(_Val), static_cast(_Last - _First))); + _CSTD memchr(_First_ptr, static_cast(_Val), _Count)); if (_Result) { if constexpr (is_pointer_v<_It>) { return _Result; diff --git a/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp b/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp index 9ff968ec58..6d5d00c1cb 100644 --- a/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_equal/test.cpp @@ -11,8 +11,8 @@ #include constexpr void smoke_test() { - using ranges::equal, ranges::equal_to; - using std::abort, std::array, std::pair, std::same_as; + using ranges::equal, ranges::equal_to, ranges::begin, ranges::end; + using std::abort, std::array, std::pair, std::same_as, std::unreachable_sentinel; array, 3> const x = {{{0, 42}, {2, 42}, {4, 42}}}; array, 3> const y = {{{13, -1}, {13, 1}, {13, 3}}}; @@ -71,6 +71,13 @@ constexpr void smoke_test() { arr2[1] = 7; assert(!equal(arr1, arr2)); } + { + // Validate unreachable_sentinel cases + int arr1[3]{0, 2, 5}; + int arr2[3]{0, 2, 5}; + assert(!equal(begin(arr1), unreachable_sentinel, begin(arr2), end(arr2))); + assert(!equal(begin(arr1), end(arr1), begin(arr2), unreachable_sentinel)); + } } int main() { diff --git a/tests/std/tests/P0896R4_ranges_alg_find/test.cpp b/tests/std/tests/P0896R4_ranges_alg_find/test.cpp index f364a6823d..3ea0e54754 100644 --- a/tests/std/tests/P0896R4_ranges_alg_find/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_find/test.cpp @@ -20,7 +20,7 @@ struct instantiator { template static constexpr void call() { - using ranges::find, ranges::iterator_t; + using ranges::find, ranges::iterator_t, ranges::begin, ranges::end; for (const auto& [value, _] : haystack) { { // Validate range overload [found case] @@ -48,15 +48,20 @@ struct instantiator { STATIC_ASSERT(same_as>); assert(result == wrapped_input.end()); } - { // Validate memchr case [found case] + { // Validate memchr case char arr[5]{4, 8, 1, -15, 125}; + + // found case auto result = find(arr, 1); assert(*result == 1); - } - { // Validate memchr case [not found case] - char arr[5]{4, 8, 1, -15, 125}; - auto result = find(arr, 10); + + // not found case + result = find(arr, 10); assert(result == end(arr)); + + // unreachable_sentinel case + result = find(begin(arr), unreachable_sentinel, 1); + assert(*result == 1); } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp b/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp index 5bc0e34358..d3fcc5dedc 100644 --- a/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_lexicographical_compare/test.cpp @@ -26,7 +26,7 @@ struct instantiator { template static constexpr void call() { - using ranges::lexicographical_compare, ranges::less; + using ranges::lexicographical_compare, ranges::less, ranges::begin, ranges::end; // Validate range overload { @@ -202,6 +202,24 @@ struct instantiator { arr2[2] = 1; assert(!lexicographical_compare(arr1, arr2)); } + { // Validate memcmp + unreachable_sentinel cases + unsigned char arr1[3]{0, 1, 2}; + unsigned char arr2[3]{0, 1, 3}; + + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr1), unreachable_sentinel)); + assert(!lexicographical_compare(begin(arr1), unreachable_sentinel, begin(arr1), end(arr1))); + + assert(lexicographical_compare(begin(arr1), unreachable_sentinel, begin(arr2), unreachable_sentinel)); + assert(lexicographical_compare(begin(arr1), unreachable_sentinel, begin(arr2), end(arr2))); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr2), unreachable_sentinel)); + arr2[2] = 2; + assert(!lexicographical_compare(begin(arr1), unreachable_sentinel, begin(arr2), end(arr2))); + assert(lexicographical_compare(begin(arr1), end(arr1), begin(arr2), unreachable_sentinel)); + arr2[2] = 1; + assert(!lexicographical_compare(begin(arr1), unreachable_sentinel, begin(arr2), unreachable_sentinel)); + assert(!lexicographical_compare(begin(arr1), unreachable_sentinel, begin(arr2), end(arr2))); + assert(!lexicographical_compare(begin(arr1), end(arr1), begin(arr2), unreachable_sentinel)); + } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/env.lst b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/env.lst index d6d824b587..18e2d7c71e 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/env.lst +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\concepts_20_matrix.lst +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp index 3096d91b16..d648315e59 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_copy_n/test.cpp @@ -209,6 +209,31 @@ struct memcpy_test { assert(equal(input, expected_input)); assert(equal(output, expected_input_short)); } + + { // Validate unreachable_sentinel + vector input = {13, 55, 12345, 42}; + vector output = {-1, -1, -1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_copy_n(input.begin(), 3, output.begin(), unreachable_sentinel); + assert(next(result.in) == input.end()); + assert(next(result.out) == output.end()); + assert(equal(input, expected_input)); + assert(equal(output, expected_output)); + } + + { // Validate non-common range + vector input = {13, 55, 12345, 42}; + vector output = {-1, -1, -1, -1}; + + auto wrapped_output = output | views::take_while([](const auto&) { return true; }); + const same_as>, iterator_t>>> auto result = + uninitialized_copy_n(input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(next(result.in) == input.end()); + assert(next(result.out) == output.end()); + assert(equal(input, expected_input)); + assert(equal(output, expected_output)); + } } }; diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/env.lst b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/env.lst index d6d824b587..18e2d7c71e 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/env.lst +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/env.lst @@ -1,4 +1,4 @@ # Copyright (c) Microsoft Corporation. # SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception -RUNALL_INCLUDE ..\concepts_20_matrix.lst +RUNALL_INCLUDE ..\concepts_latest_matrix.lst diff --git a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/test.cpp b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/test.cpp index 60d6a79c73..ecc0f60e57 100644 --- a/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/test.cpp +++ b/tests/std/tests/P0896R4_ranges_alg_uninitialized_move_n/test.cpp @@ -210,6 +210,31 @@ struct memcpy_test { assert(equal(input, expected_input)); assert(equal(output, expected_input_short)); } + + { // Validate unreachable_sentinel + vector input = {13, 55, 12345, 42}; + vector output = {-1, -1, -1, -1}; + + const same_as>, iterator_t>>> auto result = + uninitialized_move_n(input.begin(), 3, output.begin(), unreachable_sentinel); + assert(next(result.in) == input.end()); + assert(next(result.out) == output.end()); + assert(equal(input, expected_input)); + assert(equal(output, expected_output)); + } + + { // Validate non-common range + vector input = {13, 55, 12345, 42}; + vector output = {-1, -1, -1, -1}; + + auto wrapped_output = output | views::take_while([](const auto&) { return true; }); + const same_as>, iterator_t>>> auto result = + uninitialized_move_n(input.begin(), 3, wrapped_output.begin(), wrapped_output.end()); + assert(next(result.in) == input.end()); + assert(next(result.out) == output.end()); + assert(equal(input, expected_input)); + assert(equal(output, expected_output)); + } } };