diff --git a/stl/inc/iterator b/stl/inc/iterator index 19d67597d39..6c36d930059 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -534,21 +534,20 @@ public: requires convertible_to constexpr counted_iterator(const counted_iterator<_Other>& _Right) noexcept( is_nothrow_constructible_v<_Iter, const _Other&>) // strengthened - : _Current(_Right._Current), _Length(_Right._Length) {} + : _Current(_Right.base()), _Length(_Right.count()) {} template requires assignable_from<_Iter&, const _Other&> constexpr counted_iterator& operator=(const counted_iterator<_Other>& _Right) noexcept( is_nothrow_assignable_v<_Iter&, const _Other&>) /* strengthened */ { // clang-format on - _Current = _Right._Current; - _Length = _Right._Length; + _Current = _Right.base(); + _Length = _Right.count(); return *this; } // [counted.iter.access] - _NODISCARD constexpr _Iter base() const& noexcept(is_nothrow_copy_constructible_v<_Iter>) /* strengthened */ - requires copy_constructible<_Iter> { + _NODISCARD constexpr const _Iter& base() const& noexcept /* strengthened */ { // Per LWG-3391 return _Current; } @@ -720,8 +719,8 @@ public: template _Other> friend constexpr void iter_swap(const counted_iterator& _Left, const counted_iterator<_Other>& _Right) noexcept( - noexcept(_RANGES iter_swap(_Left._Current, _Right._Current))) { - _RANGES iter_swap(_Left._Current, _Right._Current); + noexcept(_RANGES iter_swap(_Left._Current, _Right.base()))) { + _RANGES iter_swap(_Left._Current, _Right.base()); } template _Other> @@ -750,7 +749,7 @@ public: template _Other> friend constexpr void _Verify_range(const counted_iterator& _Left, const counted_iterator<_Other>& _Right) { if constexpr (_Range_verifiable_v<_Iter, _Other>) { - _Verify_range(_Left._Current, _Right._Current); + _Verify_range(_Left._Current, _Right.base()); } #if _ITERATOR_DEBUG_LEVEL != 0 _Same_sequence(_Left, _Right); @@ -784,8 +783,8 @@ public: requires _Wrapped_seekable_v<_Iter, const _Other&> constexpr void _Seek_to(const counted_iterator<_Other>& _It) { // clang-format on - _Current._Seek_to(_It._Current); - _Length = _It._Length; + _Current._Seek_to(_It.base()); + _Length = _It.count(); } // clang-format off @@ -793,14 +792,11 @@ public: requires _Wrapped_seekable_v<_Iter, _Other> constexpr void _Seek_to(counted_iterator<_Other>&& _It) { // clang-format on - _Current._Seek_to(_STD move(_It)._Current); - _Length = _It._Length; + _Current._Seek_to(_STD move(_It).base()); + _Length = _It.count(); } private: - template - friend class counted_iterator; - _Iter _Current{}; iter_difference_t<_Iter> _Length = 0; }; @@ -814,6 +810,17 @@ template struct iterator_traits> : iterator_traits<_Iter> { using pointer = void; }; + +template +struct pointer_traits> { // TRANSITION, address LWG-3408 and include this + using pointer = counted_iterator<_Iter>; + using element_type = remove_reference_t>; + using difference_type = iter_difference_t<_Iter>; + + _NODISCARD static constexpr element_type* to_address(const pointer _It) noexcept { + return _STD to_address(_It.base()); + } +}; #endif // __cpp_lib_concepts _STD_END diff --git a/stl/inc/ranges b/stl/inc/ranges index 7e03c9e6a03..7feab0c5678 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -12,6 +12,9 @@ #pragma message("The contents of are available only with C++20 concepts support.") #else // ^^^ !defined(__cpp_lib_concepts) / defined(__cpp_lib_concepts) vvv #include +#include +#include + #if 1 // TRANSITION, VSO-1174090 #include #endif // TRANSITION, VSO-1174090 @@ -47,6 +50,12 @@ namespace ranges { && (is_pointer_v<_It> || _Has_member_arrow<_It&>); // clang-format on + template + using _Maybe_const = conditional_t<_IsConst, const _Ty, _Ty>; + + template + using _Maybe_wrapped = conditional_t<_IsWrapped, _Ty, _Unwrapped_t<_Ty>>; + namespace _Pipe { // clang-format off template @@ -999,6 +1008,420 @@ namespace ranges { inline constexpr _Filter_fn filter; } // namespace views + // CLASS TEMPLATE ranges::take_view + template + class take_view : public view_interface> { + private: + /* [[no_unique_address]] */ _Vw _Range{}; + range_difference_t<_Vw> _Count = 0; + + template + class _Sentinel { + private: + template + friend class _Sentinel; + + using _Base_Ty = _Maybe_const<_Const, _Vw>; + using _Base_sentinel = _Maybe_wrapped<_Wrapped, sentinel_t<_Base_Ty>>; + using _Counted_Iter = counted_iterator<_Maybe_wrapped<_Wrapped, iterator_t<_Base_Ty>>>; + + _Base_sentinel _Last{}; + + public: + _Sentinel() = default; + + constexpr explicit _Sentinel(_Base_sentinel _Last_) noexcept( + is_nothrow_move_constructible_v<_Base_sentinel>) // strengthened + : _Last(_STD move(_Last_)) {} + + // clang-format off + constexpr _Sentinel(_Sentinel _That) noexcept( + is_nothrow_constructible_v<_Base_sentinel, _Maybe_wrapped<_Wrapped, sentinel_t<_Vw>>>) // strengthened + requires _Const && convertible_to<_Maybe_wrapped<_Wrapped, sentinel_t<_Vw>>, _Base_sentinel> + : _Last(_STD move(_That._Last)) {} + // clang-format on + + _NODISCARD constexpr _Base_sentinel base() const + noexcept(is_nothrow_copy_constructible_v<_Base_sentinel>) /* strengthened */ { + return _Last; + } + + _NODISCARD friend constexpr bool operator==(const _Counted_Iter& _Left, const _Sentinel& _Right) { + return _Left.count() == 0 || _Left.base() == _Right._Last; + } + + using _Prevent_inheriting_unwrap = _Sentinel; + + // clang-format off + _NODISCARD constexpr auto _Unwrapped() const& + requires _Wrapped && _Unwrappable_v&> { + // clang-format on + return _Sentinel<_Const, false>{_Get_unwrapped(_Last)}; + } + // clang-format off + _NODISCARD constexpr auto _Unwrapped() && requires _Wrapped && _Unwrappable_v> { + // clang-format on + return _Sentinel<_Const, false>{_Get_unwrapped(_STD move(_Last))}; + } + + static constexpr bool _Unwrap_when_unverified = _Do_unwrap_when_unverified_v>; + + constexpr void _Seek_to(const _Sentinel<_Const, false>& _That) requires _Wrapped { + _Seek_wrapped(_Last, _That._Last); + } + constexpr void _Seek_to(_Sentinel<_Const, false>&& _That) requires _Wrapped { + _Seek_wrapped(_Last, _STD move(_That._Last)); + } + }; + + public: + take_view() = default; + + constexpr take_view(_Vw _Range_, const range_difference_t<_Vw> _Count_) noexcept( + is_nothrow_move_constructible_v<_Vw>) // strengthened + : _Range(_STD move(_Range_)), _Count{_Count_} {} + + _NODISCARD constexpr _Vw base() const& noexcept( + is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { + return _Range; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + // clang-format off + _NODISCARD constexpr auto begin() requires (!_Simple_view<_Vw>) { + // clang-format on + if constexpr (sized_range<_Vw>) { + if constexpr (random_access_range<_Vw>) { + return _RANGES begin(_Range); + } else { + const auto _Size = size(); + return counted_iterator{_RANGES begin(_Range), _Size}; + } + } else { + return counted_iterator{_RANGES begin(_Range), _Count}; + } + } + + _NODISCARD constexpr auto begin() const requires range { + if constexpr (sized_range) { + if constexpr (random_access_range) { + return _RANGES begin(_Range); + } else { + const auto _Size = size(); + return counted_iterator{_RANGES begin(_Range), _Size}; + } + } else { + return counted_iterator{_RANGES begin(_Range), _Count}; + } + } + + // clang-format off + _NODISCARD constexpr auto end() requires (!_Simple_view<_Vw>) { + // clang-format on + if constexpr (sized_range<_Vw>) { + if constexpr (random_access_range<_Vw>) { + return _RANGES begin(_Range) + static_cast>(size()); + } else { + return default_sentinel; + } + } else { + return _Sentinel{_RANGES end(_Range)}; + } + } + + _NODISCARD constexpr auto end() const requires range { + if constexpr (sized_range) { + if constexpr (random_access_range) { + return _RANGES begin(_Range) + static_cast>(size()); + } else { + return default_sentinel; + } + } else { + return _Sentinel{_RANGES end(_Range)}; + } + } + + _NODISCARD constexpr auto size() requires sized_range<_Vw> { + const auto _Length = _RANGES size(_Range); + return (_STD min)(_Length, static_cast(_Count)); + } + + _NODISCARD constexpr auto size() const requires sized_range { + const auto _Length = _RANGES size(_Range); + return (_STD min)(_Length, static_cast(_Count)); + } + }; + + template // Per P/R of LWG-3447 + take_view(_Rng&&, range_difference_t<_Rng>)->take_view>; + + namespace views { + // VARIABLE views::take + template + static constexpr bool _Is_dynamic_span = false; + template + static constexpr bool _Is_dynamic_span> = true; + + template + static constexpr bool _Is_subrange = false; + template + static constexpr bool _Is_subrange> = true; + + template + concept _Reconstructible_range = random_access_range<_Rng> && sized_range<_Rng> + && (_Is_dynamic_span> + // || _Is_specialization_v, basic_string_view> // TRANSITION, P1391R4 + // || _Is_specialization_v, iota_view> // TRANSITION, iota_view + || _Is_subrange>); + + class _Take_fn { + private: + enum class _St { _Empty, _Preserve, _Take_view }; + + template + _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { + using _Ty = remove_cvref_t<_Rng>; + + if constexpr (_Is_specialization_v<_Ty, empty_view>) { + return {_St::_Empty, true}; + } else if constexpr (_Reconstructible_range<_Rng>) { + return {_St::_Preserve, + noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()), + _RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>())})}; + } else { + return {_St::_Take_view, noexcept(take_view{_STD declval<_Rng>(), range_difference_t<_Rng>{0}})}; + } + } + + template + static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>(); + + template <_Integer_like _Ty> + struct _Partial : _Pipe::_Base<_Partial<_Ty>> { + _Ty _Length; + + // clang-format off + template + requires convertible_to<_Ty&, range_difference_t<_Rng>> + _NODISCARD constexpr auto operator()(_Rng&& _Range) const + noexcept(noexcept(_Take_fn{}(_STD forward<_Rng>(_Range), _Length))) { + // clang-format on + _STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>); + return _Take_fn{}(_STD forward<_Rng>(_Range), _Length); + } + }; + + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range, range_difference_t<_Rng> _Count) const noexcept( + _Choice<_Rng>._No_throw) { + // clang-format on + if constexpr (_Choice<_Rng>._Strategy == _St::_Empty) { + // it's an empty_view: return another empty view + return remove_cvref_t<_Rng>{}; + } else if constexpr (_Choice<_Rng>._Strategy == _St::_Preserve) { + // it's a "reconstructible range"; return the same kind of range with a restricted extent + _Count = (_STD min)(_RANGES distance(_Range), _Count); + const auto _First = _RANGES begin(_Range); + return remove_cvref_t<_Rng>{_First, _First + _Count}; + } else if constexpr (_Choice<_Rng>._Strategy == _St::_Take_view) { + return take_view{_STD forward<_Rng>(_Range), _Count}; + } + } + + template <_Integer_like _Ty> + _NODISCARD constexpr auto operator()(_Ty _Length) const noexcept { + return _Partial<_Ty>{._Length = _Length}; + } + }; + + inline constexpr _Take_fn take; + } // namespace views + + // CLASS TEMPLATE ranges::drop_view + template + class drop_view : public _Cached_position_t && !(random_access_range<_Vw> && sized_range<_Vw>), + _Vw, drop_view<_Vw>> { + private: + /* [[no_unique_address]] */ _Vw _Range{}; + range_difference_t<_Vw> _Count = 0; + + public: + drop_view() = default; + + constexpr drop_view(_Vw _Range_, const range_difference_t<_Vw> _Count_) noexcept( + is_nothrow_move_constructible_v<_Vw>) // strengthened + : _Range(_STD move(_Range_)), _Count{_Count_} { +#if _CONTAINER_DEBUG_LEVEL > 0 + _STL_VERIFY(_Count_ >= 0, "Numer of elements to drop must be non-negative (N4861 [range.drop.view]/1"); +#endif // _CONTAINER_DEBUG_LEVEL > 0 + } + + _NODISCARD constexpr _Vw base() const& noexcept( + is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { + return _Range; + } + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + // clang-format off + _NODISCARD constexpr auto begin() // constraints per proposed resolution of LWG-3482 + requires (!(_Simple_view<_Vw> && random_access_range && sized_range)) { + // clang-format on + if constexpr (sized_range<_Vw> && random_access_range<_Vw>) { + const auto _Offset = (_STD min)(_RANGES distance(_Range), _Count); + return _RANGES begin(_Range) + _Offset; + } else { + if constexpr (forward_range<_Vw>) { + if (this->_Has_cache()) { + return this->_Get_cache(_Range); + } + } + + iterator_t<_Vw> _Result; + if constexpr (sized_range<_Vw>) { + auto _Offset = _RANGES distance(_Range); + if constexpr (bidirectional_range<_Vw> && common_range<_Vw>) { + if (_Count >= _Offset / 2) { + _Result = _RANGES end(_Range); + while (_Offset > _Count) { + --_Offset; + --_Result; + } + + this->_Set_cache(_Range, _Result); + return _Result; + } + } + + if (_Offset > _Count) { + _Offset = _Count; + } + + _Result = _RANGES next(_RANGES begin(_Range), _Offset); + } else { + _Result = _RANGES next(_RANGES begin(_Range), _Count, _RANGES end(_Range)); + } + + if constexpr (forward_range<_Vw>) { + this->_Set_cache(_Range, _Result); + } + return _Result; + } + } + + // clang-format off + _NODISCARD constexpr auto begin() const // constraints per proposed resolution of LWG-3482 + requires random_access_range && sized_range { + // clang-format on + const auto _Offset = (_STD min)(_RANGES distance(_Range), _Count); + return _RANGES begin(_Range) + _Offset; + } + + // clang-format off + _NODISCARD constexpr auto end() requires (!_Simple_view<_Vw>) { + // clang-format on + return _RANGES end(_Range); + } + + _NODISCARD constexpr auto end() const requires range { + return _RANGES end(_Range); + } + + _NODISCARD constexpr auto size() requires sized_range<_Vw> { + const auto _Size = _RANGES size(_Range); + const auto _Count_as_size = static_cast>(_Count); + if (_Size < _Count_as_size) { + return range_size_t<_Vw>{0}; + } else { + return static_cast>(_Size - _Count_as_size); + } + } + + _NODISCARD constexpr auto size() const requires sized_range { + const auto _Size = _RANGES size(_Range); + const auto _Count_as_size = static_cast>(_Count); + if (_Size < _Count_as_size) { + return range_size_t<_Vw>{0}; + } else { + return static_cast>(_Size - _Count_as_size); + } + } + }; + + template + drop_view(_Rng&&, range_difference_t<_Rng>) -> drop_view>; + + namespace views { + // VARIABLE views::drop + class _Drop_fn { + private: + enum class _St { _Empty, _Preserve, _Drop_view }; + + template + _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { + using _Ty = remove_cvref_t<_Rng>; + + if constexpr (_Is_specialization_v<_Ty, empty_view>) { + return {_St::_Empty, true}; + } else if constexpr (_Reconstructible_range<_Rng>) { + return {_St::_Preserve, + noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()), + _RANGES end(_STD declval<_Rng&>())})}; + } else { + return {_St::_Drop_view, noexcept(drop_view{_STD declval<_Rng>(), range_difference_t<_Rng>{0}})}; + } + } + + template + static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>(); + + template <_Integer_like _Ty> + struct _Partial : _Pipe::_Base<_Partial<_Ty>> { + _Ty _Length; + + // clang-format off + template + requires convertible_to<_Ty&, range_difference_t<_Rng>> + _NODISCARD constexpr auto operator()(_Rng&& _Range) const + noexcept(noexcept(_Drop_fn{}(_STD forward<_Rng>(_Range), _Length))) { + // clang-format on + _STL_INTERNAL_STATIC_ASSERT(is_aggregate_v<_Partial>); + return _Drop_fn{}(_STD forward<_Rng>(_Range), _Length); + } + }; + + public: + // clang-format off + template + _NODISCARD constexpr auto operator()(_Rng&& _Range, range_difference_t<_Rng> _Count) const noexcept( + _Choice<_Rng>._No_throw) { + // clang-format on + if constexpr (_Choice<_Rng>._Strategy == _St::_Empty) { + // it's an empty_view: return another empty view + return remove_cvref_t<_Rng>{}; + } else if constexpr (_Choice<_Rng>._Strategy == _St::_Preserve) { + // it's a "reconstructible range"; return the same kind of range with a restricted extent + _Count = (_STD min)(_RANGES distance(_Range), _Count); + return remove_cvref_t<_Rng>{_RANGES begin(_Range) + _Count, _RANGES end(_Range)}; + } else if constexpr (_Choice<_Rng>._Strategy == _St::_Drop_view) { + return drop_view{_STD forward<_Rng>(_Range), _Count}; + } + } + + template <_Integer_like _Ty> + _NODISCARD constexpr auto operator()(_Ty _Length) const noexcept { + return _Partial<_Ty>{._Length = _Length}; + } + }; + + inline constexpr _Drop_fn drop; + } // namespace views + // CLASS TEMPLATE ranges::reverse_view // clang-format off template diff --git a/stl/inc/xutility b/stl/inc/xutility index 3a0b57d327b..f35a2ace52c 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -3257,7 +3257,8 @@ namespace ranges { using _Not_quite_object::_Not_quite_object; template _Se> - _NODISCARD constexpr iter_difference_t<_It> operator()(_It _First, _Se _Last) const { + _NODISCARD constexpr iter_difference_t<_It> operator()(_It _First, _Se _Last) const + noexcept(_Nothrow_distance<_It, _Se>) /* strengthened */ { if constexpr (sized_sentinel_for<_Se, _It>) { return _Last - _First; } else { @@ -3267,7 +3268,8 @@ namespace ranges { } template - _NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Range) const { + _NODISCARD constexpr range_difference_t<_Rng> operator()(_Rng&& _Range) const + noexcept(_Nothrow_size<_Rng>) /* strengthened */ { if constexpr (sized_range<_Rng>) { return static_cast>(_RANGES size(_Range)); } else { @@ -3277,7 +3279,8 @@ namespace ranges { private: template - _NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) { + _NODISCARD static constexpr iter_difference_t<_It> _Distance_unchecked(_It _First, const _Se _Last) noexcept( + _Nothrow_distance<_It, _Se>) { _STL_INTERNAL_STATIC_ASSERT(input_or_output_iterator<_It>); _STL_INTERNAL_STATIC_ASSERT(sentinel_for<_Se, _It>); @@ -3288,6 +3291,18 @@ namespace ranges { return _Count; } + + template + static constexpr bool _Nothrow_distance = noexcept( + noexcept(++_STD declval<_Unwrapped_t<_It>&>() != _STD declval&>())); + template _Se> + static constexpr bool _Nothrow_distance<_It, _Se> = noexcept( + noexcept(_STD declval<_Se&>() - _STD declval<_It&>())); + + template + static constexpr bool _Nothrow_size = _Nothrow_distance, sentinel_t<_Rng>>; + template + static constexpr bool _Nothrow_size<_Rng> = noexcept(_RANGES size(_STD declval<_Rng&>())); }; inline constexpr _Distance_fn distance{_Not_quite_object::_Construct_tag{}}; @@ -3991,10 +4006,10 @@ public: // clang-format on #ifdef __cpp_lib_concepts - _NODISCARD constexpr const iterator_type& base() const& { // Per LWG-3391 + _NODISCARD constexpr const iterator_type& base() const& noexcept /* strengthened */ { // Per LWG-3391 return _Current; } - _NODISCARD constexpr iterator_type base() && { + _NODISCARD constexpr iterator_type base() && noexcept(is_nothrow_move_constructible_v<_Iter>) /* strengthened */ { return _STD move(_Current); } #else // ^^^ __cpp_lib_concepts / !__cpp_lib_concepts vvv diff --git a/tests/std/include/range_algorithm_support.hpp b/tests/std/include/range_algorithm_support.hpp index d1d48e17274..9ddd8fd4ea8 100644 --- a/tests/std/include/range_algorithm_support.hpp +++ b/tests/std/include/range_algorithm_support.hpp @@ -340,6 +340,22 @@ namespace test { static constexpr bool at_least = derived_from; using ReferenceType = conditional_t, Element&>; + + struct post_increment_proxy { + Element* ptr_; + + const post_increment_proxy& operator*() const noexcept { + return *this; + } + + template + requires std::indirectly_writable + const post_increment_proxy& operator=(T&& t) const noexcept { + *ptr_ = std::forward(t); + return *this; + } + }; + public: using Consterator = iterator; @@ -385,10 +401,11 @@ namespace test { ++ptr_; return *this; } - constexpr iterator operator++(int) & noexcept { - auto tmp = *this; + + constexpr post_increment_proxy operator++(int) & noexcept requires std::is_same_v { + post_increment_proxy result{ptr_}; ++ptr_; - return tmp; + return result; } auto operator--() & { @@ -428,6 +445,13 @@ namespace test { ranges::iter_swap(x.ptr_, y.ptr_); } + // forward iterator operations: + constexpr iterator operator++(int) & noexcept requires at_least { + auto tmp = *this; + ++ptr_; + return tmp; + } + // sentinel operations (implied by forward iterator): iterator(iterator const&) requires (to_bool(Eq)) = default; iterator& operator=(iterator const&) requires (to_bool(Eq)) = default; diff --git a/tests/std/test.lst b/tests/std/test.lst index c02408a767f..7e6cc4ff99c 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -320,11 +320,13 @@ tests\P0896R4_ranges_subrange tests\P0896R4_ranges_test_machinery tests\P0896R4_ranges_to_address tests\P0896R4_views_all +tests\P0896R4_views_drop tests\P0896R4_views_empty tests\P0896R4_views_filter tests\P0896R4_views_filter_death tests\P0896R4_views_reverse tests\P0896R4_views_single +tests\P0896R4_views_take tests\P0898R3_concepts tests\P0898R3_identity tests\P0912R5_coroutine diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index 32aa5b7d4ab..8e5eb5bd9b2 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -90,9 +90,11 @@ STATIC_ASSERT(test_cpo(ranges::data)); STATIC_ASSERT(test_cpo(ranges::cdata)); STATIC_ASSERT(test_cpo(ranges::views::all)); +STATIC_ASSERT(test_cpo(ranges::views::drop)); STATIC_ASSERT(test_cpo(ranges::views::filter)); STATIC_ASSERT(test_cpo(ranges::views::reverse)); STATIC_ASSERT(test_cpo(ranges::views::single)); +STATIC_ASSERT(test_cpo(ranges::views::take)); void test_cpo_ambiguity() { using namespace std::ranges; diff --git a/tests/std/tests/P0896R4_views_drop/env.lst b/tests/std/tests/P0896R4_views_drop/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_drop/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_drop/test.cpp b/tests/std/tests/P0896R4_views_drop/test.cpp new file mode 100644 index 00000000000..a7ebf021ece --- /dev/null +++ b/tests/std/tests/P0896R4_views_drop/test.cpp @@ -0,0 +1,511 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +#pragma warning(disable : 6011) // Dereferencing NULL pointer '%s' + +// Test a silly precomposed range adaptor pipeline +constexpr auto pipeline = views::drop(1) | views::drop(1) | views::drop(1) | views::drop(1); + +template +inline constexpr bool is_empty_view = false; +template +inline constexpr bool is_empty_view> = true; + +template +inline constexpr bool is_dynamic_span = false; +template +inline constexpr bool is_dynamic_span> = true; + +template +inline constexpr bool is_string_view = false; +template +inline constexpr bool is_string_view> = true; + +template +inline constexpr bool is_subrange = false; +template +inline constexpr bool is_subrange> = true; + +// clang-format off +template +concept reconstructible = ranges::random_access_range + && ranges::sized_range + && (is_empty_view + || is_dynamic_span + // || is_string_view // TRANSITION, P1391R4 + // || is_iota_view // TRANSITION, iota_view + || is_subrange); +// clang-format on + +template +using mapped_t = conditional_t, V, ranges::drop_view>; + +template +using pipeline_t = mapped_t>>>>; + +template +concept CanViewDrop = requires(Rng&& r) { + views::drop(static_cast(r), 42); +}; + +template +constexpr bool test_one(Rng&& rng, Expected&& expected) { + using ranges::drop_view, ranges::common_range, ranges::enable_borrowed_range, ranges::iterator_t, ranges::prev, + ranges::range, ranges::sentinel_t, ranges::sized_range; + using ranges::input_range, ranges::forward_range, ranges::bidirectional_range, ranges::random_access_range, + ranges::contiguous_range; + + constexpr bool is_view = ranges::view>; + + using V = views::all_t; + using M = mapped_t; + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(common_range == common_range); + STATIC_ASSERT(input_range == input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(contiguous_range == contiguous_range); + + // Validate range adaptor object and range adaptor closure + constexpr auto drop_four = views::drop(4); + + // ... with lvalue argument + STATIC_ASSERT(CanViewDrop == (!is_view || copyable) ); + if constexpr (CanViewDrop) { // Validate lvalue + constexpr bool is_noexcept = !is_view || (is_nothrow_copy_constructible_v && !is_subrange); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(rng, 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as>); + STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewDrop&> == (!is_view || copyable) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = (is_nothrow_copy_constructible_v && !is_subrange); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(as_const(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } else if constexpr (!is_view) { + using RC = mapped_t&>>; + constexpr bool is_noexcept = is_nothrow_constructible_v&, int>; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(as_const(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewDrop> == is_view || enable_borrowed_range>); + if constexpr (is_view) { + constexpr bool is_noexcept = is_nothrow_move_constructible_v && !is_subrange; + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(move(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } else if constexpr (enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = drop_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(move(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as>>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewDrop> == (is_view && copyable) + || (!is_view && enable_borrowed_range>) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v && !is_subrange; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(as_const(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } else if constexpr (!is_view && enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = drop_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::drop(move(as_const(rng)), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | drop_four) == is_noexcept); + + STATIC_ASSERT(same_as>>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } + + const bool is_empty = ranges::empty(expected); + + // Validate deduction guide +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + same_as> auto r = drop_view{forward(rng), 4}; + using R = decltype(r); + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(input_range == input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(contiguous_range == contiguous_range); + + // Validate drop_view::size + STATIC_ASSERT(CanMemberSize == CanSize); + if constexpr (CanMemberSize) { + assert(r.size() == static_cast(ranges::size(expected))); + } else { + STATIC_ASSERT(!CanSize); + } + + // Validate view_interface::empty and operator bool + STATIC_ASSERT(CanMemberEmpty == forward_range); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } else { + STATIC_ASSERT(CanEmpty == CanSize); + if constexpr (CanEmpty) { + assert(ranges::empty(r) == is_empty); + assert(static_cast(r) == !is_empty); + } + } + + STATIC_ASSERT(CanMemberEmpty == (random_access_range && sized_range) ); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } else { + STATIC_ASSERT(CanEmpty == CanSize); + if constexpr (CanEmpty) { + assert(ranges::empty(as_const(r)) == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + } + + // Validate content + assert(ranges::equal(r, expected)); + + // Validate drop_view::begin + STATIC_ASSERT(CanMemberBegin); + STATIC_ASSERT(same_as, iterator_t>); + STATIC_ASSERT(CanBegin == (random_access_range && sized_range) ); + if (forward_range) { // intentionally not if constexpr + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + + if constexpr (random_access_range && sized_range) { + STATIC_ASSERT(same_as, iterator_t>); + const same_as> auto i3 = as_const(r).begin(); + if (!is_empty) { + assert(*i3 == *i); + } + } + } + + // Validate drop_view::end + STATIC_ASSERT(CanMemberEnd); + STATIC_ASSERT(same_as, sentinel_t>); + STATIC_ASSERT(CanEnd == (random_access_range && sized_range) ); + if (!is_empty) { + same_as> auto i = r.end(); + if constexpr (bidirectional_range && common_range) { + assert(*prev(i) == *prev(end(expected))); + } + + if constexpr (random_access_range && sized_range) { + same_as> auto i2 = as_const(r).end(); + if constexpr (bidirectional_range && common_range) { + assert(*prev(i2) == *prev(end(expected))); + } + } + } + + // Validate view_interface::data + STATIC_ASSERT(CanMemberData == contiguous_range); + STATIC_ASSERT(CanData == contiguous_range); + STATIC_ASSERT(CanData == (contiguous_range && sized_range) ); + if constexpr (contiguous_range) { + const same_as>*> auto ptr1 = r.data(); + assert(to_address(ptr1) == to_address(r.begin())); + + if constexpr (CanData) { + const same_as>*> auto ptr2 = as_const(r).data(); + assert(to_address(ptr2) == to_address(as_const(r).begin())); + } + } + + // Validate view_interface::front and back + if (!is_empty) { + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (forward_range) { + assert(r.front() == *begin(expected)); + } + + STATIC_ASSERT(CanMemberFront == (random_access_range && sized_range) ); + if constexpr (CanMemberFront) { + assert(as_const(r).front() == *begin(expected)); + } + + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected))); + } + + STATIC_ASSERT( + CanMemberBack == (common_range && random_access_range && sized_range) ); + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected))); + } + } + + // Validate view_interface::operator[] + STATIC_ASSERT(CanIndex == random_access_range); + STATIC_ASSERT(CanIndex == (random_access_range && sized_range) ); + if (!is_empty) { + if constexpr (CanIndex) { + assert(r[0] == *r.begin()); + } + if constexpr (CanIndex) { + assert(as_const(r)[0] == *as_const(r).begin()); + } + } + + // Validate drop_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible); + if constexpr (CanMemberBase && forward_range) { + same_as auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); + if (!is_empty) { + assert(*b1.begin() == 0); // NB: depends on the test data + if constexpr (bidirectional_range && common_range) { + assert(*prev(b1.end()) == 7); // NB: depends on the test data + } + } + } + + // Validate drop_view::base() && (NB: do this last since it leaves r moved-from) + if (forward_range) { // intentionally not if constexpr + same_as auto b2 = move(r).base(); + STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v); + if (!is_empty) { + assert(*b2.begin() == 0); // NB: depends on the test data + if constexpr (bidirectional_range && common_range) { + assert(*prev(b2.end()) == 7); // NB: depends on the test data + } + } + } + + return true; +} + +static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; +static constexpr int only_four_ints[] = {4, 5, 6, 7}; + +struct instantiator { + template + static constexpr void call() { + R r{some_ints}; + test_one(r, only_four_ints); + + R empty_range{}; + test_one(empty_range, span{}); + } +}; + +template +using test_range = + test::range}, + IsCommon, test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; + +constexpr void instantiation_test() { +#ifdef TEST_EVERYTHING + test_in(); +#else // ^^^ test all input range permutations / test only "interesting" permutations vvv + // The view is sensitive to category, size, and commonality, but oblivious to differencing and proxyness. + using test::Common, test::Sized; + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); +#endif // TEST_EVERYTHING +} + +template +using move_only_view = test::range}, IsCommon, + test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; + +constexpr void move_only_test() { + using test::Common, test::Sized; + using input = input_iterator_tag; + using fwd = forward_iterator_tag; + using bidi = bidirectional_iterator_tag; + using random = random_access_iterator_tag; + using contiguous = contiguous_iterator_tag; + + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); +} + +constexpr void output_range_test() { +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 + if (!is_constant_evaluated()) +#endif // TRANSITION, VSO-938163 + { + using R = test::range; + int some_writable_ints[] = {0, 1, 2, 3}; + STATIC_ASSERT(same_as>); + ranges::fill(R{some_writable_ints} | views::drop(2), 42); + assert(ranges::equal(some_writable_ints, initializer_list{0, 1, 42, 42})); + } +} + +int main() { + // Validate views + { // ... copyable + // Test all of the "reconstructible range" types: span, empty_view, subrange, basic_string_view, iota_view + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, only_four_ints)); + test_one(s, only_four_ints); + + STATIC_ASSERT(test_one(ranges::subrange{some_ints}, only_four_ints)); + test_one(ranges::subrange{some_ints}, only_four_ints); + + STATIC_ASSERT(test_one(views::empty, span{})); + test_one(views::empty, span{}); + + // TRANSITION, P1391R4 + // STATIC_ASSERT(test_one(basic_string_view{some_ints}, only_four_ints)); + // test_one(basic_string_view{some_ints}, only_four_ints); + + // TRANSITION, iota_view + // STATIC_ASSERT(test_one(ranges::iota_view{0, 8}, only_four_ints)); + // test_one(ranges::iota_view{0, 8}, only_four_ints); + } + // ... move-only + STATIC_ASSERT((move_only_test(), true)); + move_only_test(); + + // Validate non-views + { + STATIC_ASSERT(test_one(some_ints, only_four_ints)); + test_one(some_ints, only_four_ints); + } + { + vector vec(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(vec, only_four_ints); + } + { + forward_list lst(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(lst, only_four_ints); + } + + // Validate a non-view borrowed range + { + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, only_four_ints)); + test_one(s, only_four_ints); + } + + // Validate an output range + STATIC_ASSERT((output_range_test(), true)); + output_range_test(); + + STATIC_ASSERT((instantiation_test(), true)); + instantiation_test(); +} diff --git a/tests/std/tests/P0896R4_views_take/env.lst b/tests/std/tests/P0896R4_views_take/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_take/env.lst @@ -0,0 +1,4 @@ +# Copyright (c) Microsoft Corporation. +# SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +RUNALL_INCLUDE ..\strict_concepts_matrix.lst diff --git a/tests/std/tests/P0896R4_views_take/test.cpp b/tests/std/tests/P0896R4_views_take/test.cpp new file mode 100644 index 00000000000..56bc5334922 --- /dev/null +++ b/tests/std/tests/P0896R4_views_take/test.cpp @@ -0,0 +1,534 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include +using namespace std; + +#pragma warning(disable : 6011) // Dereferencing NULL pointer '%s' + +// Test a silly precomposed range adaptor pipeline +constexpr auto pipeline = views::take(7) | views::take(6) | views::take(5) | views::take(4); + +template +inline constexpr bool is_empty_view = false; +template +inline constexpr bool is_empty_view> = true; + +template +inline constexpr bool is_dynamic_span = false; +template +inline constexpr bool is_dynamic_span> = true; + +template +inline constexpr bool is_string_view = false; +template +inline constexpr bool is_string_view> = true; + +template +inline constexpr bool is_subrange = false; +template +inline constexpr bool is_subrange> = true; + +// clang-format off +template +concept reconstructible = ranges::random_access_range + && ranges::sized_range + && (is_empty_view + || is_dynamic_span + // || is_string_view // TRANSITION, P1391R4 + // || is_iota_view // TRANSITION, iota_view + || is_subrange); +// clang-format on + +template +using mapped_t = conditional_t, V, ranges::take_view>; + +template +using pipeline_t = mapped_t>>>>; + +template +concept CanViewTake = requires(Rng&& r) { + views::take(static_cast(r), 42); +}; + +template +constexpr bool test_one(Rng&& rng, Expected&& expected) { + using ranges::input_range, ranges::forward_range, ranges::bidirectional_range, ranges::random_access_range, + ranges::contiguous_range; + using ranges::take_view, ranges::common_range, ranges::enable_borrowed_range, ranges::iterator_t, ranges::prev, + ranges::range, ranges::sentinel_t, ranges::sized_range; + + constexpr bool is_view = ranges::view>; + + using V = views::all_t; + using M = mapped_t; + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(input_range == input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(contiguous_range == contiguous_range); + + // Validate range adaptor object and range adaptor closure + constexpr auto take_four = views::take(4); + + // ... with lvalue argument + STATIC_ASSERT(CanViewTake == (!is_view || copyable) ); + if constexpr (CanViewTake) { // Validate lvalue + constexpr bool is_noexcept = !is_view || (is_nothrow_copy_constructible_v && !is_subrange); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(rng, 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(rng | take_four) == is_noexcept); + + STATIC_ASSERT(same_as>); + STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); + } + + // ... with const lvalue argument + STATIC_ASSERT(CanViewTake&> == (!is_view || copyable) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = (is_nothrow_copy_constructible_v && !is_subrange); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(as_const(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | take_four) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } else if constexpr (!is_view) { + using RC = mapped_t&>>; + constexpr bool is_noexcept = is_nothrow_constructible_v&, int>; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(as_const(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | take_four) == is_noexcept); + + STATIC_ASSERT(same_as&>>); + STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); + } + + // ... with rvalue argument + STATIC_ASSERT(CanViewTake> == is_view || enable_borrowed_range>); + if constexpr (is_view) { + constexpr bool is_noexcept = is_nothrow_move_constructible_v && !is_subrange; + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(move(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | take_four) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } else if constexpr (enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = take_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(move(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(rng) | take_four) == is_noexcept); + + STATIC_ASSERT(same_as>>>); + STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); + } + + // ... with const rvalue argument + STATIC_ASSERT(CanViewTake> == (is_view && copyable) + || (!is_view && enable_borrowed_range>) ); + if constexpr (is_view && copyable) { + constexpr bool is_noexcept = is_nothrow_copy_constructible_v && !is_subrange; + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(as_const(rng), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(as_const(rng) | take_four) == is_noexcept); + + STATIC_ASSERT(same_as>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } else if constexpr (!is_view && enable_borrowed_range>) { + using S = decltype(ranges::subrange{declval>()}); + using RS = take_view; + constexpr bool is_noexcept = noexcept(S{declval>()}); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(views::take(move(as_const(rng)), 4)) == is_noexcept); + + STATIC_ASSERT(same_as); + STATIC_ASSERT(noexcept(move(as_const(rng)) | take_four) == is_noexcept); + + STATIC_ASSERT(same_as>>>); + STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); + } + + const bool is_empty = ranges::empty(expected); + + // Validate deduction guide +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1159442 + (void) 42; +#endif // TRANSITION, DevCom-1159442 + same_as> auto r = take_view{forward(rng), 4}; + using R = decltype(r); + STATIC_ASSERT(ranges::view); + STATIC_ASSERT(input_range == input_range); + STATIC_ASSERT(forward_range == forward_range); + STATIC_ASSERT(bidirectional_range == bidirectional_range); + STATIC_ASSERT(random_access_range == random_access_range); + STATIC_ASSERT(contiguous_range == contiguous_range); + + // Validate take_view::size + STATIC_ASSERT(CanMemberSize == CanSize); + if constexpr (CanMemberSize) { + assert(r.size() == static_cast(ranges::size(expected))); + } else { + STATIC_ASSERT(!CanSize); + } + + // Validate view_interface::empty and operator bool + STATIC_ASSERT(CanMemberEmpty == forward_range); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(r.empty() == is_empty); + assert(static_cast(r) == !is_empty); + } else { + STATIC_ASSERT(CanEmpty == CanSize); + if constexpr (CanEmpty) { + assert(ranges::empty(r) == is_empty); + assert(static_cast(r) == !is_empty); + } + } + + STATIC_ASSERT(CanMemberEmpty == forward_range); + STATIC_ASSERT(CanBool == CanEmpty); + if constexpr (CanMemberEmpty) { + assert(as_const(r).empty() == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } else { + STATIC_ASSERT(CanEmpty == CanSize); + if constexpr (CanEmpty) { + assert(ranges::empty(as_const(r)) == is_empty); + assert(static_cast(as_const(r)) == !is_empty); + } + } + + // Validate content + assert(ranges::equal(r, expected)); + + // Validate take_view::begin + STATIC_ASSERT(CanMemberBegin); + if constexpr (random_access_range && sized_range) { + STATIC_ASSERT(same_as, iterator_t>); + } else { + STATIC_ASSERT(same_as, counted_iterator>>); + } + STATIC_ASSERT(CanBegin == range); + if (forward_range) { // intentionally not if constexpr + const same_as> auto i = r.begin(); + if (!is_empty) { + assert(*i == *begin(expected)); + } + + if constexpr (copyable) { + auto r2 = r; + const same_as> auto i2 = r2.begin(); + if (!is_empty) { + assert(*i2 == *i); + } + } + + if constexpr (range) { + if constexpr (random_access_range && sized_range) { + STATIC_ASSERT(same_as, iterator_t>); + } else { + STATIC_ASSERT(same_as, counted_iterator>>); + } + + const same_as> auto i3 = as_const(r).begin(); + if (!is_empty) { + assert(*i3 == *i); + } + } + } + + // Validate take_view::end + STATIC_ASSERT(CanMemberEnd); + if constexpr (sized_range) { + if constexpr (random_access_range) { + STATIC_ASSERT(same_as, iterator_t>); + } else { + STATIC_ASSERT(same_as, default_sentinel_t>); + } + } else { + // Not much we can do here + STATIC_ASSERT(!same_as, iterator_t>); + STATIC_ASSERT(!same_as, default_sentinel_t>); + STATIC_ASSERT(is_class_v>); + } + STATIC_ASSERT(CanEnd == range); + if (!is_empty) { + same_as> auto i = r.end(); + if constexpr (bidirectional_range && common_range) { + assert(*prev(i) == *prev(end(expected))); + } + + if constexpr (range) { + same_as> auto i2 = as_const(r).end(); + if constexpr (bidirectional_range && common_range) { + assert(*prev(i2) == *prev(end(expected))); + } + } + } + + // Validate view_interface::data + STATIC_ASSERT(CanMemberData == contiguous_range); + STATIC_ASSERT(CanData == contiguous_range); + STATIC_ASSERT(CanData == contiguous_range); + if constexpr (contiguous_range) { + const same_as>*> auto ptr1 = r.data(); + assert(to_address(ptr1) == to_address(r.begin())); + + if constexpr (contiguous_range) { + const same_as>*> auto ptr2 = as_const(r).data(); + assert(to_address(ptr2) == to_address(as_const(r).begin())); + } + } + + // Validate view_interface::front and back + if (!is_empty) { + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (forward_range) { + assert(r.front() == *begin(expected)); + } + + STATIC_ASSERT(CanMemberFront == forward_range); + if constexpr (forward_range) { + assert(as_const(r).front() == *begin(expected)); + } + + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(r.back() == *prev(end(expected))); + } + + STATIC_ASSERT(CanMemberBack == (bidirectional_range && common_range) ); + if constexpr (CanMemberBack) { + assert(as_const(r).back() == *prev(end(expected))); + } + } + + // Validate view_interface::operator[] + STATIC_ASSERT(CanIndex == random_access_range); + STATIC_ASSERT(CanIndex == random_access_range); + if (!is_empty) { + if constexpr (CanIndex) { + assert(r[0] == *r.begin()); + } + if constexpr (CanIndex) { + assert(as_const(r)[0] == *as_const(r).begin()); + } + } + + // Validate take_view::base() const& + STATIC_ASSERT(CanMemberBase == copy_constructible); + if constexpr (copy_constructible && forward_range) { + same_as auto b1 = as_const(r).base(); + STATIC_ASSERT(noexcept(as_const(r).base()) == is_nothrow_copy_constructible_v); + if (!is_empty) { + assert(*b1.begin() == *begin(expected)); + if constexpr (bidirectional_range && common_range) { + assert(*prev(b1.end()) == 7); // NB: depends on the test data + } + } + } + + // Validate take_view::base() && (NB: do this last since it leaves r moved-from) + if (forward_range) { // intentionally not if constexpr + same_as auto b2 = move(r).base(); + STATIC_ASSERT(noexcept(move(r).base()) == is_nothrow_move_constructible_v); + if (!is_empty) { + assert(*b2.begin() == *begin(expected)); + if constexpr (bidirectional_range && common_range) { + assert(*prev(b2.end()) == 7); // NB: depends on the test data + } + } + } + + return true; +} + +static constexpr int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; +static constexpr int only_four_ints[] = {0, 1, 2, 3}; + +struct instantiator { + template + static constexpr void call() { + R r{some_ints}; + test_one(r, only_four_ints); + + R empty_range{}; + test_one(empty_range, span{}); + } +}; + +template +using test_range = + test::range}, + IsCommon, test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}>; + +constexpr void instantiation_test() { +#ifdef TEST_EVERYTHING + test_in(); +#else // ^^^ test all input range permutations / test only "interesting" permutations vvv + // The view is sensitive to category, size, and commonality, but oblivious to differencing and proxyness. + using test::Common, test::Sized; + + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); + instantiator::call>(); +#endif // TEST_EVERYTHING +} + +template +using move_only_view = test::range}, IsCommon, + test::CanCompare{derived_from || IsCommon == test::Common::yes}, + test::ProxyRef{!derived_from}, test::CanView::yes, test::Copyability::move_only>; + +constexpr void move_only_test() { + using test::Common, test::Sized; + using input = input_iterator_tag; + using fwd = forward_iterator_tag; + using bidi = bidirectional_iterator_tag; + using random = random_access_iterator_tag; + using contiguous = contiguous_iterator_tag; + + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); + test_one(move_only_view{some_ints}, only_four_ints); +} + +constexpr void output_range_test() { +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, VSO-938163 + if (!is_constant_evaluated()) +#endif // TRANSITION, VSO-938163 + { + using R = test::range; + int some_writable_ints[] = {0, 1, 2, 3}; + STATIC_ASSERT(same_as>); + + // How do I implement "Fill up to n elements in {output range} with {value}"? + ranges::fill(R{some_writable_ints} | views::take(99999), 42); + assert(ranges::equal(some_writable_ints, initializer_list{42, 42, 42, 42})); + + ranges::fill(R{some_writable_ints} | views::take(3), 13); + assert(ranges::equal(some_writable_ints, initializer_list{13, 13, 13, 42})); + } +} + +int main() { + // Validate views + { // ... copyable + // Test all of the "reconstructible range" types: span, empty_view, subrange, basic_string_view, iota_view + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, only_four_ints)); + test_one(s, only_four_ints); + + STATIC_ASSERT(test_one(ranges::subrange{some_ints}, only_four_ints)); + test_one(ranges::subrange{some_ints}, only_four_ints); + + STATIC_ASSERT(test_one(views::empty, span{})); + test_one(views::empty, span{}); + + // TRANSITION, P1391R4 + // STATIC_ASSERT(test_one(basic_string_view{some_ints}, only_four_ints)); + // test_one(basic_string_view{some_ints}, only_four_ints); + + // TRANSITION, iota_view + // STATIC_ASSERT(test_one(ranges::iota_view{0, 8}, only_four_ints)); + // test_one(ranges::iota_view{0, 8}, only_four_ints); + } + // ... move-only + STATIC_ASSERT((move_only_test(), true)); + move_only_test(); + + // Validate non-views + { + STATIC_ASSERT(test_one(some_ints, only_four_ints)); + test_one(some_ints, only_four_ints); + } + { + vector vec(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(vec, only_four_ints); + } + { + forward_list lst(ranges::begin(some_ints), ranges::end(some_ints)); + test_one(lst, only_four_ints); + } + + // Validate a non-view borrowed range + { + constexpr span s{some_ints}; + STATIC_ASSERT(test_one(s, only_four_ints)); + test_one(s, only_four_ints); + } + + // Validate an output range + STATIC_ASSERT((output_range_test(), true)); + output_range_test(); + + STATIC_ASSERT((instantiation_test(), true)); + instantiation_test(); +}