diff --git a/stl/inc/ranges b/stl/inc/ranges index 1720c95babd..63c57fbe9d3 100644 --- a/stl/inc/ranges +++ b/stl/inc/ranges @@ -13,6 +13,7 @@ #else // ^^^ !defined(__cpp_lib_concepts) / defined(__cpp_lib_concepts) vvv #include #include +#include #include #include @@ -534,6 +535,331 @@ namespace ranges { inline constexpr _Single_fn single; } // namespace views + // CLASS TEMPLATE ranges::iota_view + template + using _Iota_diff_t = conditional_t, conditional_t<(sizeof(_Ty) < sizeof(int)), int, long long>, + iter_difference_t<_Ty>>; + + // clang-format off + template + concept _Decrementable = incrementable<_Ty> && requires(_Ty __t) { + { --__t } -> same_as<_Ty&>; + { __t-- } -> same_as<_Ty>; + }; + + template + concept _Advanceable = _Decrementable<_Ty> && totally_ordered<_Ty> + && requires(_Ty __i, const _Ty __j, const _Iota_diff_t<_Ty> __n) { + { __i += __n } -> same_as<_Ty&>; + { __i -= __n } -> same_as<_Ty&>; + _Ty(__j + __n); + _Ty(__n + __j); + _Ty(__j - __n); + { __j - __j } -> convertible_to<_Iota_diff_t<_Ty>>; + }; + + template + requires semiregular<_Wi> + struct _Ioterator { + // clang-format on + /* [[no_unique_address]] */ _Wi _Current{}; + + using iterator_concept = conditional_t<_Advanceable<_Wi>, random_access_iterator_tag, + conditional_t<_Decrementable<_Wi>, bidirectional_iterator_tag, + conditional_t, forward_iterator_tag, input_iterator_tag>>>; + using iterator_category = input_iterator_tag; + using value_type = _Wi; + using difference_type = _Iota_diff_t<_Wi>; + + _NODISCARD constexpr _Wi operator*() const noexcept(is_nothrow_copy_constructible_v<_Wi>) { + return _Current; + } + + constexpr _Ioterator& operator++() noexcept(noexcept(++_Current)) /* strengthened */ { + ++_Current; + return *this; + } + + constexpr auto operator++(int) noexcept( + noexcept(++_Current) && (!incrementable<_Wi> || is_nothrow_copy_constructible_v<_Wi>) ) /* strengthened */ { + if constexpr (incrementable<_Wi>) { + auto _Tmp = *this; + ++_Current; + return _Tmp; + } else { + ++_Current; + } + } + + constexpr _Ioterator& operator--() noexcept( + noexcept(--_Current)) /* strengthened */ requires _Decrementable<_Wi> { + --_Current; + return *this; + } + + constexpr _Ioterator operator--(int) noexcept(is_nothrow_copy_constructible_v<_Wi>&& noexcept( + --_Current)) /* strengthened */ requires _Decrementable<_Wi> { + auto _Tmp = *this; + --_Current; + return _Tmp; + } + +#if !defined(__clang__) && !defined(__EDG__) // TRANSITION, DevCom-1347136 + private: + template + static constexpr bool _Nothrow_plus_equal = noexcept(_STD declval<_Left&>() += _STD declval()); + template <_Integer_like _Left, class _Right> + static constexpr bool _Nothrow_plus_equal<_Left, _Right> = true; + + template + static constexpr bool _Nothrow_minus_equal = noexcept(_STD declval<_Left&>() -= _STD declval()); + template <_Integer_like _Left, class _Right> + static constexpr bool _Nothrow_minus_equal<_Left, _Right> = true; + + public: +#endif // TRANSITION, DevCom-1347136 + + constexpr _Ioterator& operator+=(const difference_type _Off) +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-1347136 + noexcept(noexcept(_Current += _Off)) /* strengthened */ +#else // ^^^ no workaround / workaround vvv + noexcept(_Nothrow_plus_equal<_Wi, difference_type>) /* strengthened */ +#endif // TRANSITION, DevCom-1347136 + requires _Advanceable<_Wi> { + if constexpr (_Integer_like<_Wi>) { + if constexpr (_Signed_integer_like<_Wi>) { + _Current = static_cast<_Wi>(_Current + _Off); + } else { + if (_Off >= difference_type{0}) { + _Current += static_cast<_Wi>(_Off); + } else { + _Current -= static_cast<_Wi>(-_Off); + } + } + } else { + _Current += _Off; + } + return *this; + } + + constexpr _Ioterator& operator-=(const difference_type _Off) +#if defined(__clang__) || defined(__EDG__) // TRANSITION, DevCom-1347136 + noexcept(noexcept(_Current -= _Off)) /* strengthened */ +#else // ^^^ no workaround / workaround vvv + noexcept(_Nothrow_minus_equal<_Wi, difference_type>) /* strengthened */ +#endif // TRANSITION, DevCom-1347136 + requires _Advanceable<_Wi> { + if constexpr (_Integer_like<_Wi>) { + if constexpr (_Signed_integer_like<_Wi>) { + _Current = static_cast<_Wi>(_Current - _Off); + } else { + if (_Off >= difference_type{0}) { + _Current -= static_cast<_Wi>(_Off); + } else { + _Current += static_cast<_Wi>(-_Off); + } + } + } else { + _Current -= _Off; + } + return *this; + } + + _NODISCARD constexpr _Wi operator[](const difference_type _Idx) const + noexcept(noexcept(static_cast<_Wi>(_Current + _Idx))) /* strengthened */ requires _Advanceable<_Wi> { + if constexpr (_Integer_like<_Wi>) { + return static_cast<_Wi>(_Current + static_cast<_Wi>(_Idx)); + } else { + return static_cast<_Wi>(_Current + _Idx); + } + } + + _NODISCARD friend constexpr bool operator==( + const _Ioterator&, const _Ioterator&) requires equality_comparable<_Wi> = default; + _NODISCARD friend constexpr bool operator<(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(_Left._Current < _Right._Current)) /* strengthened */ requires totally_ordered<_Wi> { + return _Left._Current < _Right._Current; + } + _NODISCARD friend constexpr bool operator>(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(_Right._Current < _Left._Current)) /* strengthened */ requires totally_ordered<_Wi> { + return _Right._Current < _Left._Current; + } + _NODISCARD friend constexpr bool operator<=(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(!(_Right._Current < _Left._Current))) /* strengthened */ requires totally_ordered<_Wi> { + return !(_Right._Current < _Left._Current); + } + _NODISCARD friend constexpr bool operator>=(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(!(_Left._Current < _Right._Current))) /* strengthened */ requires totally_ordered<_Wi> { + return !(_Left._Current < _Right._Current); + } + // clang-format off + _NODISCARD friend constexpr auto operator<=>(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(_Left._Current <=> _Right._Current)) /* strengthened */ + requires totally_ordered<_Wi> && three_way_comparable<_Wi> { + // clang-format on + return _Left._Current <=> _Right._Current; + } + + _NODISCARD friend constexpr _Ioterator operator+(_Ioterator _It, const difference_type _Off) noexcept( + noexcept(static_cast<_Wi>(_It._Current + _Off))) /* strengthened */ requires _Advanceable<_Wi> { + return _Ioterator{static_cast<_Wi>(_It._Current + _Off)}; + } + _NODISCARD friend constexpr _Ioterator operator+(const difference_type _Off, _Ioterator _It) noexcept( + noexcept(static_cast<_Wi>(_It._Current + _Off))) /* strengthened */ requires _Advanceable<_Wi> { + return _Ioterator{static_cast<_Wi>(_It._Current + _Off)}; + } + _NODISCARD friend constexpr _Ioterator operator-(_Ioterator _It, const difference_type _Off) noexcept( + noexcept(static_cast<_Wi>(_It._Current - _Off))) /* strengthened */ requires _Advanceable<_Wi> { + return _Ioterator{static_cast<_Wi>(_It._Current - _Off)}; + } + _NODISCARD friend constexpr difference_type + operator-(const _Ioterator& _Left, const _Ioterator& _Right) noexcept( + noexcept(_Left._Current - _Right._Current)) /* strengthened */ requires _Advanceable<_Wi> { + return static_cast(_Left._Current - _Right._Current); + } + }; + + // clang-format off + template + requires _Weakly_equality_comparable_with<_Wi, _Bo> && semiregular<_Wi> + struct _Iotinel { + // clang-format on + private: + using _It = _Ioterator<_Wi>; + + _NODISCARD constexpr bool _Equal(const _It& _That) const noexcept(noexcept(_That._Current == _Last)) { + return _That._Current == _Last; + } + + _NODISCARD constexpr iter_difference_t<_Wi> _Delta(const _It& _That) const + noexcept(noexcept(_Last - _That._Current)) { + _STL_INTERNAL_STATIC_ASSERT(sized_sentinel_for<_Bo, _Wi>); + return _Last - _That._Current; + } + + public: + /* [[no_unique_address]] */ _Bo _Last{}; + + _NODISCARD friend constexpr bool operator==(const _It& _Left, const _Iotinel& _Right) noexcept( + noexcept(_Right._Equal(_Left))) /* strengthened */ { + return _Right._Equal(_Left); + } + + _NODISCARD friend constexpr iter_difference_t<_Wi> operator-(const _It& _Left, const _Iotinel& _Right) noexcept( + noexcept(_Right._Delta(_Left))) /* strengthened */ requires sized_sentinel_for<_Bo, _Wi> { + return -_Right._Delta(_Left); + } + + _NODISCARD friend constexpr iter_difference_t<_Wi> operator-(const _Iotinel& _Left, const _It& _Right) noexcept( + noexcept(_Left._Delta(_Right))) /* strengthened */ requires sized_sentinel_for<_Bo, _Wi> { + return _Left._Delta(_Right); + } + }; + + // clang-format off + template + requires _Weakly_equality_comparable_with<_Wi, _Bo> && semiregular<_Wi> + class iota_view : public view_interface> { + // clang-format on + private: + /* [[no_unique_address]] */ _Wi _Value{}; + /* [[no_unique_address]] */ _Bo _Bound{}; + + using _It = _Ioterator<_Wi>; + using _Se = conditional_t, _It, + conditional_t, _Bo, _Iotinel<_Wi, _Bo>>>; + + _NODISCARD static constexpr _Bo& _Bound_from(_Se& _Last) noexcept { + if constexpr (same_as<_Wi, _Bo>) { + return _Last._Current; + } else if constexpr (same_as<_Bo, unreachable_sentinel_t>) { + return _Last; + } else { + return _Last._Last; + } + } + + public: + iota_view() = default; + + constexpr explicit iota_view(_Wi _Value_) noexcept( + is_nothrow_move_constructible_v<_Wi>&& is_nothrow_default_constructible_v<_Bo>) // strengthened + : _Value(_STD move(_Value_)) {} + + constexpr iota_view(type_identity_t<_Wi> _Value_, type_identity_t<_Bo> _Bound_) noexcept( + is_nothrow_move_constructible_v<_Wi>&& is_nothrow_move_constructible_v<_Bo>) // strengthened + : _Value(_STD move(_Value_)), _Bound(_STD move(_Bound_)) { + if constexpr (totally_ordered_with<_Wi, _Bo>) { + _STL_ASSERT(_Value_ <= _Bound_, "Per N4878 [range.iota.view]/8, the first argument must precede the " + "second when their types are totally ordered."); + } + } + + constexpr iota_view(_It _First, _Se _Last) noexcept( // Per LWG-3523 + is_nothrow_move_constructible_v<_Wi>&& is_nothrow_move_constructible_v<_Bo>) // strengthened + : _Value(_STD move(_First._Current)), _Bound(_STD move(_Bound_from(_Last))) {} + + _NODISCARD constexpr _It begin() const noexcept(is_nothrow_copy_constructible_v<_Wi>) /* strengthened */ { + return _It{_Value}; + } + + _NODISCARD constexpr _Se end() const noexcept(is_nothrow_copy_constructible_v<_Bo>) /* strengthened */ { + if constexpr (same_as<_Bo, unreachable_sentinel_t>) { + return unreachable_sentinel; + } else { + return _Se{_Bound}; + } + } + + // clang-format off + _NODISCARD constexpr auto size() const noexcept(noexcept(_Bound - _Value)) /* strengthened */ + requires (same_as<_Wi, _Bo> && _Advanceable<_Wi>) + || (integral<_Wi> && integral<_Bo>) + || sized_sentinel_for<_Bo, _Wi> { + // clang-format on + if constexpr (_Integer_like<_Wi> && _Integer_like<_Bo>) { +#pragma warning(suppress : 4146) // unary minus operator applied to unsigned type, result still unsigned + return (_Value < 0) ? ((_Bound < 0) ? (_To_unsigned_like(-_Value) - _To_unsigned_like(-_Bound)) +#pragma warning(suppress : 4146) // unary minus operator applied to unsigned type, result still unsigned + : (_To_unsigned_like(_Bound) + _To_unsigned_like(-_Value))) + : (_To_unsigned_like(_Bound) - _To_unsigned_like(_Value)); + } else { + return _To_unsigned_like(_Bound - _Value); + } + } + }; + + // clang-format off + template + requires (!_Integer_like<_Wi> || !_Integer_like<_Bo> + || (_Signed_integer_like<_Wi> == _Signed_integer_like<_Bo>)) + iota_view(_Wi, _Bo) -> iota_view<_Wi, _Bo>; + // clang-format on + + template + inline constexpr bool enable_borrowed_range> = true; + + namespace views { + // VARIABLE views::iota + struct _Iota_fn { + template + _NODISCARD constexpr auto operator()(_Ty&& _Val) const + noexcept(noexcept(iota_view{static_cast<_Ty&&>(_Val)})) requires requires { + iota_view{static_cast<_Ty&&>(_Val)}; + } + { return iota_view{static_cast<_Ty&&>(_Val)}; } + + template + _NODISCARD constexpr auto operator()(_Ty1&& _Val1, _Ty2&& _Val2) const noexcept( + noexcept(iota_view{static_cast<_Ty1&&>(_Val1), static_cast<_Ty2&&>(_Val2)})) requires requires { + iota_view{static_cast<_Ty1&&>(_Val1), static_cast<_Ty2&&>(_Val2)}; + } + { return iota_view{static_cast<_Ty1&&>(_Val1), static_cast<_Ty2&&>(_Val2)}; } + }; + + inline constexpr _Iota_fn iota; + } // namespace views + // CLASS TEMPLATE ranges::istream_view template concept _Stream_extractable = requires(basic_istream<_Elem, _Traits>& __is, _Ty& __t) { @@ -1666,25 +1992,25 @@ namespace ranges { 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; + inline constexpr bool _Is_subrange = false; template - static constexpr bool _Is_subrange> = true; + inline constexpr bool _Is_subrange> = true; + // clang-format off template - concept _Reconstructible_range = random_access_range<_Rng> && sized_range<_Rng> - && (_Is_dynamic_span> - || _Is_specialization_v, basic_string_view> - // || _Is_specialization_v, iota_view> // TRANSITION, iota_view - || _Is_subrange>); + concept _Random_sized_range = random_access_range<_Rng> && sized_range<_Rng>; + // clang-format on class _Take_fn { private: - enum class _St { _Empty, _Preserve, _Take_view }; + enum class _St { + _Empty, + _Reconstruct_span, + _Reconstruct_string_view, + _Reconstruct_iota_view, + _Reconstruct_subrange, + _Take_view + }; template _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { @@ -1692,9 +2018,16 @@ namespace ranges { 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&>()), + } else if constexpr (_Is_span_v<_Ty>) { + return {_St::_Reconstruct_span, true}; + } else if constexpr (_Is_specialization_v<_Ty, basic_string_view>) { + return {_St::_Reconstruct_string_view, true}; + } else if constexpr (_Random_sized_range<_Ty> && _Is_specialization_v<_Ty, iota_view>) { + return {_St::_Reconstruct_iota_view, + noexcept(_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()))}; + } else if constexpr (_Random_sized_range<_Ty> && _Is_subrange<_Ty>) { + return {_St::_Reconstruct_subrange, + noexcept(subrange{_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}})}; @@ -1730,13 +2063,26 @@ namespace ranges { if constexpr (_Strat == _St::_Empty) { // it's an empty_view: return another empty view return remove_cvref_t<_Rng>{}; - } else if constexpr (_Strat == _St::_Preserve) { + } else if constexpr (_Strat == _St::_Take_view) { + return take_view{_STD forward<_Rng>(_Range), _Count}; + } else { // 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 (_Strat == _St::_Take_view) { - return take_view{_STD forward<_Rng>(_Range), _Count}; + + // The following are all per the proposed resolution of LWG-3407 + if constexpr (_Strat == _St::_Reconstruct_span) { + return span{_First, _First + _Count}; + } else if constexpr (_Strat == _St::_Reconstruct_string_view) { + return remove_cvref_t<_Rng>{_First, _First + _Count}; + } else if constexpr (_Strat == _St::_Reconstruct_iota_view) { + using _Vt = range_value_t<_Rng>; + return iota_view<_Vt, _Vt>{_First, _First + _Count}; + } else if constexpr (_Strat == _St::_Reconstruct_subrange) { + return subrange{_First, _First + _Count}; + } else { + static_assert(_Always_false<_Rng>, "Should be unreachable"); + } } } @@ -2053,7 +2399,7 @@ namespace ranges { // VARIABLE views::drop class _Drop_fn { private: - enum class _St { _Empty, _Preserve, _Drop_view }; + enum class _St { _Empty, _Reconstruct_span, _Reconstruct_subrange, _Reconstruct_other, _Drop_view }; template _NODISCARD static _CONSTEVAL _Choice_t<_St> _Choose() noexcept { @@ -2061,8 +2407,22 @@ namespace ranges { if constexpr (_Is_specialization_v<_Ty, empty_view>) { return {_St::_Empty, true}; - } else if constexpr (_Reconstructible_range<_Rng>) { - return {_St::_Preserve, + } else if constexpr (_Is_span_v<_Ty>) { + return {_St::_Reconstruct_span, true}; + } else if constexpr (_Is_specialization_v<_Ty, basic_string_view>) { + return {_St::_Reconstruct_other, true}; + } else if constexpr (_Random_sized_range<_Ty> && _Is_subrange<_Ty>) { + if constexpr (sized_sentinel_for, iterator_t<_Ty>>) { + return {_St::_Reconstruct_subrange, + noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()), + _RANGES end(_STD declval<_Rng&>())})}; + } else { + return {_St::_Reconstruct_subrange, + noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()), + _RANGES end(_STD declval<_Rng&>()), range_difference_t<_Rng>{0}})}; + } + } else if constexpr (_Random_sized_range<_Ty> && _Is_specialization_v<_Ty, iota_view>) { + return {_St::_Reconstruct_other, noexcept(_Ty{_RANGES begin(_STD declval<_Rng&>()) + _RANGES distance(_STD declval<_Rng&>()), _RANGES end(_STD declval<_Rng&>())})}; } else { @@ -2099,12 +2459,27 @@ namespace ranges { if constexpr (_Strat == _St::_Empty) { // it's an empty_view: return another empty view return remove_cvref_t<_Rng>{}; - } else if constexpr (_Strat == _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 (_Strat == _St::_Drop_view) { return drop_view{_STD forward<_Rng>(_Range), _Count}; + } else { + // it's a "reconstructible range"; return the same kind of range with a restricted extent + _Count = (_STD min)(_RANGES distance(_Range), _Count); + + // The following are all per the proposed resolution of LWG-3407 + if constexpr (_Strat == _St::_Reconstruct_span) { + return span{_Ubegin(_Range) + _Count, _Uend(_Range)}; + } else if constexpr (_Strat == _St::_Reconstruct_subrange) { + if constexpr (sized_sentinel_for, iterator_t<_Rng>>) { + return remove_cvref_t<_Rng>{_RANGES begin(_Range) + _Count, _RANGES end(_Range)}; + } else { + return remove_cvref_t<_Rng>{ + _RANGES begin(_Range) + _Count, _RANGES end(_Range), _RANGES size(_Range) - _Count}; + } + } else if constexpr (_Strat == _St::_Reconstruct_other) { + return remove_cvref_t<_Rng>{_RANGES begin(_Range) + _Count, _RANGES end(_Range)}; + } else { + static_assert(_Always_false<_Rng>, "Should be unreachable"); + } } } diff --git a/stl/inc/xutility b/stl/inc/xutility index 66e6cbd227b..2d46ee445c9 100644 --- a/stl/inc/xutility +++ b/stl/inc/xutility @@ -774,11 +774,11 @@ concept indirectly_writable = requires(_It&& __i, _Ty&& __t) { }; // CONCEPT _Integer_like -// clang-format off template -concept _Integer_like = _Is_nonbool_integral<_Ty>; +concept _Integer_like = _Is_nonbool_integral>; // CONCEPT _Signed_integer_like +// clang-format off template concept _Signed_integer_like = _Integer_like<_Ty> && static_cast<_Ty>(-1) < static_cast<_Ty>(0); // clang-format on @@ -787,10 +787,20 @@ concept _Signed_integer_like = _Integer_like<_Ty> && static_cast<_Ty>(-1) < stat template using _Make_unsigned_like_t = make_unsigned_t<_Ty>; +template <_Integer_like _Ty> +_NODISCARD constexpr auto _To_unsigned_like(const _Ty _Value) noexcept { + return static_cast<_Make_unsigned_like_t<_Ty>>(_Value); +} + // ALIAS TEMPLATE _Make_signed_like_t template using _Make_signed_like_t = make_signed_t<_Ty>; +template <_Integer_like _Ty> +_NODISCARD constexpr auto _To_signed_like(const _Ty _Value) noexcept { + return static_cast<_Make_signed_like_t<_Ty>>(_Value); +} + // CONCEPT weakly_incrementable // clang-format off template diff --git a/tests/std/test.lst b/tests/std/test.lst index ca60f536b2f..be7fe6eaf49 100644 --- a/tests/std/test.lst +++ b/tests/std/test.lst @@ -373,6 +373,7 @@ tests\P0896R4_views_elements tests\P0896R4_views_empty tests\P0896R4_views_filter tests\P0896R4_views_filter_death +tests\P0896R4_views_iota tests\P0896R4_views_reverse tests\P0896R4_views_single tests\P0896R4_views_take diff --git a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp index 576c4340517..2e4698779e3 100644 --- a/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp +++ b/tests/std/tests/P0896R4_ranges_range_machinery/test.cpp @@ -104,6 +104,7 @@ STATIC_ASSERT(test_cpo(ranges::views::drop)); STATIC_ASSERT(test_cpo(ranges::views::drop_while)); STATIC_ASSERT(test_cpo(ranges::views::elements<42>)); STATIC_ASSERT(test_cpo(ranges::views::filter)); +STATIC_ASSERT(test_cpo(ranges::views::iota)); STATIC_ASSERT(test_cpo(ranges::views::keys)); STATIC_ASSERT(test_cpo(ranges::views::reverse)); STATIC_ASSERT(test_cpo(ranges::views::single)); @@ -1499,9 +1500,7 @@ namespace borrowed_range_testing { STATIC_ASSERT(test_borrowed_range, std::span::iterator>()); STATIC_ASSERT(test_borrowed_range, int*>()); STATIC_ASSERT(test_borrowed_range, int*>()); -#if 0 // TRANSITION, future - STATIC_ASSERT(test_borrowed_range, ...>()); -#endif // TRANSITION, future + STATIC_ASSERT(test_borrowed_range, ranges::iterator_t>>()); struct simple_borrowed_range { int* begin() const { diff --git a/tests/std/tests/P0896R4_views_drop/test.cpp b/tests/std/tests/P0896R4_views_drop/test.cpp index f8d503138de..2e154446815 100644 --- a/tests/std/tests/P0896R4_views_drop/test.cpp +++ b/tests/std/tests/P0896R4_views_drop/test.cpp @@ -20,41 +20,58 @@ using namespace std; 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; +inline constexpr bool is_span = false; +template +inline constexpr bool is_span> = true; template inline constexpr bool is_subrange = false; template inline constexpr bool is_subrange> = true; +template +struct mapped { + template + using apply = ranges::drop_view>; +}; +template +struct mapped> { + template + using apply = ranges::empty_view; +}; +template +struct mapped> { + template + using apply = span; +}; +template +struct mapped> { + template + using apply = basic_string_view; +}; +// clang-format off +template + requires ranges::random_access_range> + && ranges::sized_range> +struct mapped> { + // clang-format on + template + using apply = ranges::iota_view; +}; // clang-format off -template -concept reconstructible = ranges::random_access_range - && ranges::sized_range - && (is_empty_view - || is_dynamic_span - || is_string_view - // || is_iota_view // TRANSITION, iota_view - || is_subrange); -// clang-format on - -template -using mapped_t = conditional_t, V, ranges::drop_view>; +template + requires random_access_iterator +struct mapped> { + // clang-format on + template + using apply = ranges::subrange; +}; template -using pipeline_t = mapped_t>>>>; +using mapped_t = typename mapped>::template apply; + +template +using pipeline_t = mapped_t>>>; template concept CanViewDrop = requires(Rng&& r) { @@ -71,7 +88,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { constexpr bool is_view = ranges::view>; using V = views::all_t; - using M = mapped_t; + using M = mapped_t; STATIC_ASSERT(ranges::view); STATIC_ASSERT(common_range == common_range); STATIC_ASSERT(input_range == input_range); @@ -81,7 +98,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(contiguous_range == contiguous_range); // Validate range adaptor object and range adaptor closure - constexpr auto drop_four = views::drop(4); + constexpr auto closure = views::drop(4); // ... with lvalue argument STATIC_ASSERT(CanViewDrop == (!is_view || copyable) ); @@ -91,8 +108,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { 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 | closure) == is_noexcept); STATIC_ASSERT(same_as>); STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); @@ -100,26 +117,26 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... with const lvalue argument STATIC_ASSERT(CanViewDrop&> == (!is_view || copyable) ); - if constexpr (is_view && copyable) { + if constexpr (is_span> || (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) | closure) == is_noexcept); STATIC_ASSERT(same_as&>>); STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); } else if constexpr (!is_view) { - using RC = mapped_t&>>; + 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) | closure) == is_noexcept); STATIC_ASSERT(same_as&>>); STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); @@ -127,13 +144,13 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... with rvalue argument STATIC_ASSERT(CanViewDrop> == is_view || enable_borrowed_range>); - if constexpr (is_view) { + if constexpr (is_span> || 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) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); @@ -145,8 +162,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { 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) | closure) == is_noexcept); STATIC_ASSERT(same_as>>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); @@ -155,14 +172,14 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... with const rvalue argument STATIC_ASSERT(CanViewDrop> == (is_view && copyable) || (!is_view && enable_borrowed_range>) ); - if constexpr (is_view && copyable) { + if constexpr (is_span> || (is_view && copyable) ) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v && !is_subrange; 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)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); @@ -174,8 +191,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { 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)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); @@ -455,9 +472,13 @@ 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); + constexpr span s0{some_ints}; + STATIC_ASSERT(test_one(s0, only_four_ints)); + test_one(s0, only_four_ints); + + constexpr span s1{some_ints}; + STATIC_ASSERT(test_one(s1, only_four_ints)); + test_one(s1, only_four_ints); STATIC_ASSERT(test_one(ranges::subrange{some_ints}, only_four_ints)); test_one(ranges::subrange{some_ints}, only_four_ints); @@ -468,9 +489,8 @@ int main() { STATIC_ASSERT(test_one(basic_string_view{ranges::begin(some_ints), ranges::end(some_ints)}, only_four_ints)); test_one(basic_string_view{ranges::begin(some_ints), ranges::end(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); + 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)); @@ -490,13 +510,6 @@ int main() { 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(); diff --git a/tests/std/tests/P0896R4_views_iota/env.lst b/tests/std/tests/P0896R4_views_iota/env.lst new file mode 100644 index 00000000000..62a24024479 --- /dev/null +++ b/tests/std/tests/P0896R4_views_iota/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_iota/test.cpp b/tests/std/tests/P0896R4_views_iota/test.cpp new file mode 100644 index 00000000000..ed990295ba3 --- /dev/null +++ b/tests/std/tests/P0896R4_views_iota/test.cpp @@ -0,0 +1,299 @@ +// Copyright (c) Microsoft Corporation. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception + +#include +#include +#include +#include +#include +#include + +using namespace std; + +template +concept CanViewIota = requires(W w, B b) { + views::iota(w, b); +}; + +template +concept CanSize = requires(R& r) { + ranges::size(r); +}; + +struct empty_type {}; + +template +constexpr void test_integral() { + constexpr T low = 0; + constexpr T high = 8; + constexpr T expected[] = {0, 1, 2, 3, 4, 5, 6, 7}; + + { + // Validate bounded (both upper and lower bounds exist) iota_view + using R = ranges::iota_view; + + // Validate type properties + static_assert(same_as, T>); + static_assert(same_as, T>); + + static_assert(ranges::random_access_range); + static_assert(!ranges::contiguous_range); + static_assert(ranges::common_range); + + static_assert(ranges::view); + static_assert(semiregular); + static_assert(is_nothrow_copy_constructible_v); + static_assert(is_nothrow_copy_assignable_v); + static_assert(is_nothrow_move_constructible_v); + static_assert(is_nothrow_move_assignable_v); + + if constexpr (sizeof(T) < sizeof(int)) { + static_assert(same_as, int>); + } else { + static_assert(same_as, long long>); + } + + // iota_view is always a simple-view, i.e., const and non-const are always valid ranges with the same iterators: + static_assert(ranges::common_range); + static_assert( + same_as>, ranges::iterator_t>>); + + static_assert(same_as, T>); + static_assert(same_as, ranges::range_difference_t>); + + const same_as auto rng = views::iota(low, high); + static_assert(noexcept(views::iota(low, high))); // strengthened + + assert(ranges::equal(rng, expected)); + static_assert(noexcept(rng.begin())); // strengthened + static_assert(noexcept(rng.end())); // strengthened + + assert(rng.size() == 8u); + static_assert(noexcept(rng.size() == 8u)); // strengthened + + using I = ranges::iterator_t; + static_assert(same_as); + static_assert(same_as::iterator_category, input_iterator_tag>); + + assert(I{} == I{T{0}}); + static_assert(is_nothrow_default_constructible_v); + assert(I{} == I{}); + assert(!(I{} != I{})); + assert(!(I{} < I{})); + assert(!(I{} > I{})); + assert(I{} <= I{}); + assert(I{} >= I{}); + assert(I{} <=> I{} == 0); + assert(I{} - I{} == 0); + + const I first = rng.begin(); + const I second = ranges::next(first); + + assert(*first == T{0}); + assert(*second == T{1}); + static_assert(noexcept(*first)); + + assert(first == I{T{0}}); + assert(second == I{T{1}}); + static_assert(noexcept(noexcept(I{T{0}}))); // strengthened + + { + I tmp = first; + static_assert(is_nothrow_copy_constructible_v); + + I i = move(tmp); + static_assert(is_nothrow_move_constructible_v); + + tmp = i; + static_assert(is_nothrow_copy_assignable_v); + + i = move(tmp); + static_assert(is_nothrow_move_assignable_v); + } + + assert(!(first == second)); + assert(!(second == first)); + static_assert(noexcept(first == second)); // strengthened + + assert(first != second); + assert(second != first); + static_assert(noexcept(first != second)); // strengthened + + assert(first < second); + assert(!(second < first)); + static_assert(noexcept(first < second)); // strengthened + + assert(!(first > second)); + assert(second > first); + static_assert(noexcept(first > second)); // strengthened + + assert(first <= second); + assert(!(second <= first)); + static_assert(noexcept(first <= second)); // strengthened + + assert(!(first >= second)); + assert(second >= first); + static_assert(noexcept(first >= second)); // strengthened + + assert(first <=> second < 0); + assert(second <=> first > 0); + static_assert(noexcept(first <=> second)); // strengthened + + { + I i = first; + + assert(&++i == &i); + assert(i == second); + static_assert(noexcept(++i)); // strengthened + + i = first; + + assert(i++ == first); + assert(i == second); + static_assert(noexcept(i++)); // strengthened + + assert(&--i == &i); + assert(i == first); + static_assert(noexcept(--i)); // strengthened + + i = second; + + assert(i-- == second); + assert(i == first); + static_assert(noexcept(i--)); // strengthened + + assert(i + 1 == second); + static_assert(noexcept(i + 1)); // strengthened + + assert(1 + i == second); + static_assert(noexcept(1 + i)); // strengthened + + assert(&(i += 1) == &i); + assert(i == second); + static_assert(noexcept(i += 1)); // strengthened + assert(i - 1 == first); + static_assert(noexcept(i - 1)); // strengthened + + assert(&(i -= 1) == &i); + assert(i == first); + static_assert(noexcept(i -= 1)); // strengthened + + assert(second - first == 1); + static_assert(noexcept(second - first)); // strengthened + + assert(first[1] == *second); + assert(second[-1] == *first); + static_assert(noexcept(first[1])); // strengthened + } + + const same_as> auto last = rng.end(); + assert(!(first == last)); + static_assert(noexcept(first == last)); // strengthened + assert(first != last); + static_assert(noexcept(first != last)); // strengthened + assert(last - first == 8); + static_assert(noexcept(last - first)); // strengthened + } + + { + // Validate unbounded (upper bound is unreachable) iota_view + using R = ranges::iota_view; + static_assert(same_as, T>); + static_assert(same_as, T>); + + static_assert(ranges::random_access_range); + static_assert(!ranges::contiguous_range); + static_assert(!ranges::common_range); + static_assert(same_as>, unreachable_sentinel_t>); + + // Bounded and unbounded iota_view have the same iterator type, however: + static_assert(same_as>, ranges::iterator_t>>); + + if constexpr (sizeof(T) < sizeof(int)) { + static_assert(same_as, int>); + } else { + static_assert(same_as, long long>); + } + + { + const same_as auto rng = views::iota(low); + + auto i = low; + for (const auto& e : rng) { + assert(e == i); + if (++i == high) { + break; + } + } + } + + static_assert(!CanSize>); + } +} + +int main() { + // Validate standard signed integer types + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + + // Validate standard unsigned integer types + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + + // Validate other integer types +#ifndef __clang__ // TRANSITION, LLVM-48173 + static_assert(!CanViewIota); +#endif // TRANSITION, LLVM-48173 + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); +#ifdef __cpp_char8_t + static_assert((test_integral(), true)); + test_integral(); +#endif // __cpp_char8_t + static_assert((test_integral(), true)); + test_integral(); + static_assert((test_integral(), true)); + test_integral(); + + // Some non-integer coverage: + { + // Pointers + empty_type objects[] = {{}, {}, {}}; + const auto address = [](auto& x) { return &x; }; + assert(ranges::equal( + views::iota(begin(as_const(objects)), end(objects)), objects, ranges::equal_to{}, identity{}, address)); + assert(ranges::equal( + views::iota(begin(objects), end(as_const(objects))), objects, ranges::equal_to{}, identity{}, address)); + } + { + // Iterator and sentinel of a non-common range + const int some_ints[] = {0, 1, 2, 3, 4, 5, 6, 7}; + const int even_ints[] = {0, 2, 4, 6}; + + const auto even = [](int x) { return x % 2 == 0; }; + const auto deref = [](auto x) -> decltype(auto) { return *x; }; + + auto f = some_ints | views::filter(even); + auto r = views::iota(ranges::begin(f), ranges::end(f)); + + assert(ranges::equal(r, even_ints, ranges::equal_to{}, deref)); + } +} diff --git a/tests/std/tests/P0896R4_views_take/test.cpp b/tests/std/tests/P0896R4_views_take/test.cpp index 27ccb1b7cc3..34fb9e3a305 100644 --- a/tests/std/tests/P0896R4_views_take/test.cpp +++ b/tests/std/tests/P0896R4_views_take/test.cpp @@ -20,41 +20,58 @@ using namespace std; 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; +inline constexpr bool is_span = false; +template +inline constexpr bool is_span> = true; template inline constexpr bool is_subrange = false; template inline constexpr bool is_subrange> = true; +template +struct mapped { + template + using apply = ranges::take_view>; +}; +template +struct mapped> { + template + using apply = ranges::empty_view; +}; +template +struct mapped> { + template + using apply = span; +}; +template +struct mapped> { + template + using apply = basic_string_view; +}; +// clang-format off +template + requires ranges::random_access_range> + && ranges::sized_range> +struct mapped> { + // clang-format on + template + using apply = ranges::iota_view; +}; // clang-format off -template -concept reconstructible = ranges::random_access_range - && ranges::sized_range - && (is_empty_view - || is_dynamic_span - || is_string_view - // || is_iota_view // TRANSITION, iota_view - || is_subrange); -// clang-format on - -template -using mapped_t = conditional_t, V, ranges::take_view>; +template + requires random_access_iterator +struct mapped> { + // clang-format on + template + using apply = ranges::subrange; +}; + +template +using mapped_t = typename mapped>::template apply; template -using pipeline_t = mapped_t>>>>; +using pipeline_t = mapped_t>>>; template concept CanViewTake = requires(Rng&& r) { @@ -71,7 +88,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { constexpr bool is_view = ranges::view>; using V = views::all_t; - using M = mapped_t; + using M = mapped_t; STATIC_ASSERT(ranges::view); STATIC_ASSERT(input_range == input_range); STATIC_ASSERT(forward_range == forward_range); @@ -80,7 +97,7 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { STATIC_ASSERT(contiguous_range == contiguous_range); // Validate range adaptor object and range adaptor closure - constexpr auto take_four = views::take(4); + constexpr auto closure = views::take(4); // ... with lvalue argument STATIC_ASSERT(CanViewTake == (!is_view || copyable) ); @@ -90,8 +107,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { 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 | closure) == is_noexcept); STATIC_ASSERT(same_as>); STATIC_ASSERT(noexcept(rng | pipeline) == is_noexcept); @@ -99,40 +116,40 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... 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); + if constexpr (is_span> || (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) | closure) == is_noexcept); STATIC_ASSERT(same_as&>>); STATIC_ASSERT(noexcept(as_const(rng) | pipeline) == is_noexcept); } else if constexpr (!is_view) { - using RC = mapped_t&>>; + 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) | closure) == 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) { + STATIC_ASSERT(CanViewTake> == (is_view || enable_borrowed_range>) ); + if constexpr (is_span> || 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) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); @@ -144,8 +161,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { 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) | closure) == is_noexcept); STATIC_ASSERT(same_as>>>); STATIC_ASSERT(noexcept(move(rng) | pipeline) == is_noexcept); @@ -154,14 +171,14 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { // ... with const rvalue argument STATIC_ASSERT(CanViewTake> == (is_view && copyable) || (!is_view && enable_borrowed_range>) ); - if constexpr (is_view && copyable) { + if constexpr (is_span> || (is_view && copyable) ) { constexpr bool is_noexcept = is_nothrow_copy_constructible_v && !is_subrange; 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)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); @@ -173,8 +190,8 @@ constexpr bool test_one(Rng&& rng, Expected&& expected) { 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)) | closure) == is_noexcept); STATIC_ASSERT(same_as>>>); STATIC_ASSERT(noexcept(move(as_const(rng)) | pipeline) == is_noexcept); @@ -496,10 +513,14 @@ constexpr void output_range_test() { 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); + // Test all of the "reconstructible range" types: span, empty_view, subrange, basic_string_view, iota_view + constexpr span s0{some_ints}; + STATIC_ASSERT(test_one(s0, only_four_ints)); + test_one(s0, only_four_ints); + + constexpr span s1{some_ints}; + STATIC_ASSERT(test_one(s1, only_four_ints)); + test_one(s1, only_four_ints); STATIC_ASSERT(test_one(ranges::subrange{some_ints}, only_four_ints)); test_one(ranges::subrange{some_ints}, only_four_ints); @@ -510,9 +531,8 @@ int main() { STATIC_ASSERT(test_one(basic_string_view{ranges::begin(some_ints), ranges::end(some_ints)}, only_four_ints)); test_one(basic_string_view{ranges::begin(some_ints), ranges::end(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); + 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)); @@ -532,13 +552,6 @@ int main() { 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();