From 55e77fdc55474d9033cf830172b246ec8ca133e4 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Mon, 31 Aug 2020 23:32:34 -0700 Subject: [PATCH 1/6] Implement views::take and views::drop Also implements the proposed resolution of LWG-3481. --- stl/inc/iterator | 37 +- stl/inc/ranges | 440 ++++++++++++++- stl/inc/xutility | 25 +- tests/std/include/range_algorithm_support.hpp | 30 +- tests/std/test.lst | 2 + .../P0896R4_ranges_range_machinery/test.cpp | 2 + tests/std/tests/P0896R4_views_drop/env.lst | 4 + tests/std/tests/P0896R4_views_drop/test.cpp | 511 +++++++++++++++++ tests/std/tests/P0896R4_views_take/env.lst | 4 + tests/std/tests/P0896R4_views_take/test.cpp | 530 ++++++++++++++++++ 10 files changed, 1560 insertions(+), 25 deletions(-) create mode 100644 tests/std/tests/P0896R4_views_drop/env.lst create mode 100644 tests/std/tests/P0896R4_views_drop/test.cpp create mode 100644 tests/std/tests/P0896R4_views_take/env.lst create mode 100644 tests/std/tests/P0896R4_views_take/test.cpp diff --git a/stl/inc/iterator b/stl/inc/iterator index 19d67597d39..897fde34a62 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> { // TODO: 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 ead69fbcd17..e32ac0b58bd 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 @@ -29,9 +32,10 @@ namespace ranges { // clang-format off // CONCEPT ranges::viewable_range - template + template // Per P/R of LWG-3481 concept viewable_range = range<_Rng> - && (borrowed_range<_Rng> || view>); + && ((view> && constructible_from, _Rng>) + || (!view> && borrowed_range<_Rng>)); template concept _Simple_view = view<_Rng> && range @@ -42,6 +46,12 @@ namespace ranges { concept _Copy_constructible_object = copy_constructible<_Ty> && is_object_v<_Ty>; // 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 @@ -728,6 +738,432 @@ namespace ranges { using all_t = decltype(all(_STD declval<_Rng>())); } // 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 + + constexpr _Base_sentinel base() const + noexcept(is_nothrow_copy_constructible_v<_Base_sentinel>) /* strengthened */ { + return _Last; + } + + 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< iterator_t<_Base_Ty>> { + // clang-format on + return _Sentinel<_Const, false>{_Get_unwrapped(_STD move(_Last))}; + } + + static constexpr bool _Unwrap_when_unverified = _Do_unwrap_when_unverified_v>; + + // clang-format off + constexpr void _Seek_to(const _Sentinel<_Const, false>& _That) requires _Wrapped { + // clang-format on + _Seek_wrapped(_Last, _That._Last); + } + constexpr void _Seek_to(_Sentinel<_Const, false>&& _That) requires _Wrapped { + // clang-format on + _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_} {} + + constexpr _Vw base() const& noexcept( + is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { + return _Range; + } + constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + // clang-format off + 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}; + } + } + + 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 + 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)}; + } + } + + 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)}; + } + } + + constexpr auto size() requires sized_range<_Vw> { + const auto _Length = _RANGES size(_Range); + return (_STD min)(_Length, static_cast(_Count)); + } + + 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> + class _Partial : public _Pipe::_Base<_Partial<_Ty>> { + private: + _Ty _Length; + + constexpr explicit _Partial(_Ty _Length_) noexcept : _Length{_Length_} {} + + public: + friend _Take_fn; + + // 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 + 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}; + } + }; + + 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 + } + + constexpr _Vw base() const& noexcept( + is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { + return _Range; + } + constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + return _STD move(_Range); + } + + // clang-format off + constexpr auto begin() // Constraints per P/R of LWG issue unnumbered as of 2020-08-31 + 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 + constexpr auto begin() const // Constraints per P/R of LWG issue unnumbered as of 2020-08-31 + 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 + constexpr auto end() requires (!_Simple_view<_Vw>) { + // clang-format on + return _RANGES end(_Range); + } + + constexpr auto end() const requires range { + return _RANGES end(_Range); + } + + 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); + } + } + + 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> + class _Partial : public _Pipe::_Base<_Partial<_Ty>> { + private: + _Ty _Length; + + constexpr explicit _Partial(_Ty _Length_) noexcept : _Length{_Length_} {} + + public: + friend _Drop_fn; + + // 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 + 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}; + } + }; + + 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 2c9fc57aa95..fba655b2b22 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 055ef4cb0a7..e28a3a10f34 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 933fabbb469..c5fd22863ed 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -318,9 +318,11 @@ 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_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 c8d3518b70b..34564637bca 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -90,8 +90,10 @@ 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::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..284cfa64496 --- /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..ac6b1c807fd --- /dev/null +++ b/tests/std/tests/P0896R4_views_take/test.cpp @@ -0,0 +1,530 @@ +// 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})); + } +} + +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(); +} From 6aadabe2047f965eadab2da81d62e6750ed0718f Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 1 Sep 2020 12:07:01 -0700 Subject: [PATCH 2/6] P/R => proposed resolution --- stl/inc/ranges | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index e32ac0b58bd..37dbfd56ab4 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -32,7 +32,7 @@ namespace ranges { // clang-format off // CONCEPT ranges::viewable_range - template // Per P/R of LWG-3481 + template // Per proposed resolution of LWG-3481 concept viewable_range = range<_Rng> && ((view> && constructible_from, _Rng>) || (!view> && borrowed_range<_Rng>)); From 23f3ad7079bb349c63851f6ceafb6ba3e38488c2 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Tue, 1 Sep 2020 12:24:28 -0700 Subject: [PATCH 3/6] Fix overlength lines - thanks, validator! --- stl/inc/ranges | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 37dbfd56ab4..83421333d11 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -765,8 +765,8 @@ namespace ranges { : _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 + 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 @@ -783,7 +783,8 @@ namespace ranges { using _Prevent_inheriting_unwrap = _Sentinel; // clang-format off - _NODISCARD constexpr auto _Unwrapped() const& requires _Wrapped && _Unwrappable_v&> { + _NODISCARD constexpr auto _Unwrapped() const& + requires _Wrapped && _Unwrappable_v&> { // clang-format on return _Sentinel<_Const, false>{_Get_unwrapped(_Last)}; } From 8d7359c2a7e66c349a676d79d18d95444032e452 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 2 Sep 2020 00:20:37 -0700 Subject: [PATCH 4/6] Review comments --- stl/inc/iterator | 2 +- stl/inc/ranges | 61 +++++++++------------ tests/std/tests/P0896R4_views_drop/test.cpp | 4 +- tests/std/tests/P0896R4_views_take/test.cpp | 8 ++- 4 files changed, 34 insertions(+), 41 deletions(-) diff --git a/stl/inc/iterator b/stl/inc/iterator index 897fde34a62..6c36d930059 100644 --- a/stl/inc/iterator +++ b/stl/inc/iterator @@ -812,7 +812,7 @@ struct iterator_traits> : iterator_traits<_Iter> { }; template -struct pointer_traits> { // TODO: address LWG-3408 and include this +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>; diff --git a/stl/inc/ranges b/stl/inc/ranges index 83421333d11..4fef43df737 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -771,12 +771,12 @@ namespace ranges { : _Last(_STD move(_That._Last)) {} // clang-format on - constexpr _Base_sentinel base() const + _NODISCARD constexpr _Base_sentinel base() const noexcept(is_nothrow_copy_constructible_v<_Base_sentinel>) /* strengthened */ { return _Last; } - friend constexpr bool operator==(const _Counted_Iter& _Left, const _Sentinel& _Right) { + _NODISCARD friend constexpr bool operator==(const _Counted_Iter& _Left, const _Sentinel& _Right) { return _Left.count() == 0 || _Left.base() == _Right._Last; } @@ -789,7 +789,7 @@ namespace ranges { return _Sentinel<_Const, false>{_Get_unwrapped(_Last)}; } // clang-format off - _NODISCARD constexpr auto _Unwrapped() && requires _Wrapped && _Unwrappable_v< iterator_t<_Base_Ty>> { + _NODISCARD constexpr auto _Unwrapped() && requires _Wrapped && _Unwrappable_v> { // clang-format on return _Sentinel<_Const, false>{_Get_unwrapped(_STD move(_Last))}; } @@ -802,7 +802,6 @@ namespace ranges { _Seek_wrapped(_Last, _That._Last); } constexpr void _Seek_to(_Sentinel<_Const, false>&& _That) requires _Wrapped { - // clang-format on _Seek_wrapped(_Last, _STD move(_That._Last)); } }; @@ -814,16 +813,16 @@ namespace ranges { is_nothrow_move_constructible_v<_Vw>) // strengthened : _Range(_STD move(_Range_)), _Count{_Count_} {} - constexpr _Vw base() const& noexcept( + _NODISCARD constexpr _Vw base() const& noexcept( is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { return _Range; } - constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { return _STD move(_Range); } // clang-format off - constexpr auto begin() requires (!_Simple_view<_Vw>) { + _NODISCARD constexpr auto begin() requires (!_Simple_view<_Vw>) { // clang-format on if constexpr (sized_range<_Vw>) { if constexpr (random_access_range<_Vw>) { @@ -837,7 +836,7 @@ namespace ranges { } } - constexpr auto begin() const requires range { + _NODISCARD constexpr auto begin() const requires range { if constexpr (sized_range) { if constexpr (random_access_range) { return _RANGES begin(_Range); @@ -851,7 +850,7 @@ namespace ranges { } // clang-format off - constexpr auto end() requires (!_Simple_view<_Vw>) { + _NODISCARD constexpr auto end() requires (!_Simple_view<_Vw>) { // clang-format on if constexpr (sized_range<_Vw>) { if constexpr (random_access_range<_Vw>) { @@ -864,7 +863,7 @@ namespace ranges { } } - constexpr auto end() const requires range { + _NODISCARD constexpr auto end() const requires range { if constexpr (sized_range) { if constexpr (random_access_range) { return _RANGES begin(_Range) + static_cast>(size()); @@ -876,12 +875,12 @@ namespace ranges { } } - constexpr auto size() requires sized_range<_Vw> { + _NODISCARD constexpr auto size() requires sized_range<_Vw> { const auto _Length = _RANGES size(_Range); return (_STD min)(_Length, static_cast(_Count)); } - constexpr auto size() const requires sized_range { + _NODISCARD constexpr auto size() const requires sized_range { const auto _Length = _RANGES size(_Range); return (_STD min)(_Length, static_cast(_Count)); } @@ -932,21 +931,16 @@ namespace ranges { static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>(); template <_Integer_like _Ty> - class _Partial : public _Pipe::_Base<_Partial<_Ty>> { - private: + struct _Partial : _Pipe::_Base<_Partial<_Ty>> { _Ty _Length; - constexpr explicit _Partial(_Ty _Length_) noexcept : _Length{_Length_} {} - - public: - friend _Take_fn; - // 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); } }; @@ -972,7 +966,7 @@ namespace ranges { template <_Integer_like _Ty> _NODISCARD constexpr auto operator()(_Ty _Length) const noexcept { - return _Partial<_Ty>{_Length}; + return _Partial<_Ty>{._Length = _Length}; } }; @@ -998,16 +992,16 @@ namespace ranges { #endif // _CONTAINER_DEBUG_LEVEL > 0 } - constexpr _Vw base() const& noexcept( + _NODISCARD constexpr _Vw base() const& noexcept( is_nothrow_copy_constructible_v<_Vw>) /* strengthened */ requires copy_constructible<_Vw> { return _Range; } - constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { + _NODISCARD constexpr _Vw base() && noexcept(is_nothrow_move_constructible_v<_Vw>) /* strengthened */ { return _STD move(_Range); } // clang-format off - constexpr auto begin() // Constraints per P/R of LWG issue unnumbered as of 2020-08-31 + _NODISCARD constexpr auto begin() // Constraints per P/R of LWG issue unnumbered as of 2020-08-31 requires (!(_Simple_view<_Vw> && random_access_range && sized_range)) { // clang-format on if constexpr (sized_range<_Vw> && random_access_range<_Vw>) { @@ -1053,7 +1047,7 @@ namespace ranges { } // clang-format off - constexpr auto begin() const // Constraints per P/R of LWG issue unnumbered as of 2020-08-31 + _NODISCARD constexpr auto begin() const // Constraints per P/R of LWG issue unnumbered as of 2020-08-31 requires random_access_range && sized_range { // clang-format on const auto _Offset = (_STD min)(_RANGES distance(_Range), _Count); @@ -1061,16 +1055,16 @@ namespace ranges { } // clang-format off - constexpr auto end() requires (!_Simple_view<_Vw>) { + _NODISCARD constexpr auto end() requires (!_Simple_view<_Vw>) { // clang-format on return _RANGES end(_Range); } - constexpr auto end() const requires range { + _NODISCARD constexpr auto end() const requires range { return _RANGES end(_Range); } - constexpr auto size() requires sized_range<_Vw> { + _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) { @@ -1080,7 +1074,7 @@ namespace ranges { } } - constexpr auto size() const requires sized_range { + _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) { @@ -1119,21 +1113,16 @@ namespace ranges { static constexpr _Choice_t<_St> _Choice = _Choose<_Rng>(); template <_Integer_like _Ty> - class _Partial : public _Pipe::_Base<_Partial<_Ty>> { - private: + struct _Partial : _Pipe::_Base<_Partial<_Ty>> { _Ty _Length; - constexpr explicit _Partial(_Ty _Length_) noexcept : _Length{_Length_} {} - - public: - friend _Drop_fn; - // 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); } }; @@ -1158,7 +1147,7 @@ namespace ranges { template <_Integer_like _Ty> _NODISCARD constexpr auto operator()(_Ty _Length) const noexcept { - return _Partial<_Ty>{_Length}; + return _Partial<_Ty>{._Length = _Length}; } }; diff --git a/tests/std/tests/P0896R4_views_drop/test.cpp b/tests/std/tests/P0896R4_views_drop/test.cpp index 284cfa64496..a7ebf021ece 100644 --- a/tests/std/tests/P0896R4_views_drop/test.cpp +++ b/tests/std/tests/P0896R4_views_drop/test.cpp @@ -14,7 +14,7 @@ #include using namespace std; -#pragma warning(disable : 6011) // "Dereferencing NULL pointer '%s' +#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); @@ -381,7 +381,7 @@ 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. + // The view is sensitive to category, size, and commonality, but oblivious to differencing and proxyness. using test::Common, test::Sized; instantiator::call>(); diff --git a/tests/std/tests/P0896R4_views_take/test.cpp b/tests/std/tests/P0896R4_views_take/test.cpp index ac6b1c807fd..56bc5334922 100644 --- a/tests/std/tests/P0896R4_views_take/test.cpp +++ b/tests/std/tests/P0896R4_views_take/test.cpp @@ -14,7 +14,7 @@ #include using namespace std; -#pragma warning(disable : 6011) // "Dereferencing NULL pointer '%s' +#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); @@ -399,7 +399,7 @@ 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. + // The view is sensitive to category, size, and commonality, but oblivious to differencing and proxyness. using test::Common, test::Sized; instantiator::call>(); @@ -468,9 +468,13 @@ constexpr void output_range_test() { test::CanCompare::no, test::ProxyRef::yes, test::CanView::yes, test::Copyability::move_only>; 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})); } } From 27d0444f47a2c88b6cb6357380f5e16f790959a6 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 2 Sep 2020 00:38:55 -0700 Subject: [PATCH 5/6] Update stl/inc/ranges --- stl/inc/ranges | 2 -- 1 file changed, 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 4fef43df737..2e4e1a83447 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -796,9 +796,7 @@ namespace ranges { static constexpr bool _Unwrap_when_unverified = _Do_unwrap_when_unverified_v>; - // clang-format off constexpr void _Seek_to(const _Sentinel<_Const, false>& _That) requires _Wrapped { - // clang-format on _Seek_wrapped(_Last, _That._Last); } constexpr void _Seek_to(_Sentinel<_Const, false>&& _That) requires _Wrapped { From 6e2a75d5a089b560364966c295afdaa4777bd126 Mon Sep 17 00:00:00 2001 From: Casey Carter Date: Wed, 2 Sep 2020 12:06:26 -0700 Subject: [PATCH 6/6] LWG-3482 now has a number --- stl/inc/ranges | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/stl/inc/ranges b/stl/inc/ranges index 2e4e1a83447..9323d44c638 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -999,7 +999,7 @@ namespace ranges { } // clang-format off - _NODISCARD constexpr auto begin() // Constraints per P/R of LWG issue unnumbered as of 2020-08-31 + _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>) { @@ -1045,7 +1045,7 @@ namespace ranges { } // clang-format off - _NODISCARD constexpr auto begin() const // Constraints per P/R of LWG issue unnumbered as of 2020-08-31 + _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);