Tested on x86_64-pc-linux-gnu, does this look OK for trunk? More tests are needed but I figured I'd submit this now for possible consideration into GCC 14 since we're getting close to release.. All changes are confined to C++26.
-- >8 -- libstdc++-v3/ChangeLog: * include/bits/version.def (ranges_concat): Define. * include/bits/version.h: Regenerate. * include/std/ranges (__detail::__concat_reference_t): Define for C++26. (__detail::__concat_value_t): Likewise. (__detail::__concat_rvalue_reference_t): Likewise. (__detail::__concat_indirectly_readable_impl): Likewise. (__detail::__concat_indirectly_readable): Likewise. (__detail::__concatable): Likewise. (__detail::__all_but_last_common): Likewise. (__detail::__concat_is_random_access): Likewise. (__detail::__concat_is_bidirectional): Likewise. (__detail::__last_is_common): Likewise. (concat_view): Likewise. (__detail::__concat_view_iter_cat): Likewise. (concat_view::iterator): Likewise. (views::__detail::__can_concat_view): Likewise. (views::_Concat, views::concat): Likewise. * testsuite/std/ranges/concat/1.cc: New test. --- libstdc++-v3/include/bits/version.def | 8 + libstdc++-v3/include/bits/version.h | 10 + libstdc++-v3/include/std/ranges | 584 ++++++++++++++++++ libstdc++-v3/testsuite/std/ranges/concat/1.cc | 61 ++ 4 files changed, 663 insertions(+) create mode 100644 libstdc++-v3/testsuite/std/ranges/concat/1.cc diff --git a/libstdc++-v3/include/bits/version.def b/libstdc++-v3/include/bits/version.def index 5c0477fb61e..af13090c094 100644 --- a/libstdc++-v3/include/bits/version.def +++ b/libstdc++-v3/include/bits/version.def @@ -1796,6 +1796,14 @@ ftms = { }; }; +ftms = { + name = ranges_concat; + values = { + v = 202403; + cxxmin = 26; + }; +}; + // Standard test specifications. stds[97] = ">= 199711L"; stds[03] = ">= 199711L"; diff --git a/libstdc++-v3/include/bits/version.h b/libstdc++-v3/include/bits/version.h index 65e708c73fb..1f27bfe050d 100644 --- a/libstdc++-v3/include/bits/version.h +++ b/libstdc++-v3/include/bits/version.h @@ -2003,4 +2003,14 @@ #endif /* !defined(__cpp_lib_to_string) && defined(__glibcxx_want_to_string) */ #undef __glibcxx_want_to_string +#if !defined(__cpp_lib_ranges_concat) +# if (__cplusplus > 202302L) +# define __glibcxx_ranges_concat 202403L +# if defined(__glibcxx_want_all) || defined(__glibcxx_want_ranges_concat) +# define __cpp_lib_ranges_concat 202403L +# endif +# endif +#endif /* !defined(__cpp_lib_ranges_concat) && defined(__glibcxx_want_ranges_concat) */ +#undef __glibcxx_want_ranges_concat + #undef __glibcxx_want_all diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index afce818376b..28a39bf6f34 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -55,6 +55,7 @@ #define __glibcxx_want_ranges_as_const #define __glibcxx_want_ranges_as_rvalue #define __glibcxx_want_ranges_cartesian_product +#define __glibcxx_want_ranges_concat #define __glibcxx_want_ranges_chunk #define __glibcxx_want_ranges_chunk_by #define __glibcxx_want_ranges_enumerate @@ -9514,6 +9515,589 @@ namespace __detail } // namespace ranges #endif // __cpp_lib_ranges_to_container +#if __cpp_lib_ranges_concat // C++ >= C++26 +namespace ranges +{ + namespace __detail + { + template<typename... _Rs> + using __concat_reference_t = common_reference_t<range_reference_t<_Rs>...>; + + template<typename... _Rs> + using __concat_value_t = common_type_t<range_value_t<_Rs>...>; + + template<typename... _Rs> + using __concat_rvalue_reference_t + = common_reference_t<range_rvalue_reference_t<_Rs>...>; + + template<typename _Ref, typename _RRef, typename _It> + concept __concat_indirectly_readable_impl = requires (const _It __it) { + { *__it } -> convertible_to<_Ref>; + { ranges::iter_move(__it) } -> convertible_to<_RRef>; + }; + + template<typename... _Rs> + concept __concat_indirectly_readable + = common_reference_with<__concat_reference_t<_Rs...>&&, __concat_value_t<_Rs...>&> + && common_reference_with<__concat_reference_t<_Rs...>&&, + __concat_rvalue_reference_t<_Rs...>&&> + && common_reference_with<__concat_rvalue_reference_t<_Rs...>&&, + __concat_value_t<_Rs...> const&> + && (__concat_indirectly_readable_impl<__concat_reference_t<_Rs...>, + __concat_rvalue_reference_t<_Rs...>, + iterator_t<_Rs>> + && ...); + + template<typename... _Rs> + concept __concatable = requires { + typename __concat_reference_t<_Rs...>; + typename __concat_value_t<_Rs...>; + typename __concat_rvalue_reference_t<_Rs...>; + } && __concat_indirectly_readable<_Rs...>; + + template<bool _Const, typename _Range, typename... _Rs> + struct __all_but_last_common + { + static inline constexpr bool value + = requires { requires (common_range<__maybe_const_t<_Const, _Range>> + && __all_but_last_common<_Const, _Rs...>::value); }; + }; + + template<bool _Const, typename _Range> + struct __all_but_last_common<_Const, _Range> + { static inline constexpr bool value = true; }; + + template<bool _Const, typename... _Rs> + concept __concat_is_random_access = __all_random_access<_Const, _Rs...> + && __all_but_last_common<_Const, _Rs...>::value; + + template<bool _Const, typename... _Rs> + concept __concat_is_bidirectional = __all_bidirectional<_Const, _Rs...> + && __all_but_last_common<_Const, _Rs...>::value; + + template<typename _Range, typename... _Rs> + struct __last_is_common + { static inline constexpr bool value = __last_is_common<_Rs...>::value; }; + + template<typename _Range> + struct __last_is_common<_Range> + { static inline constexpr bool value = common_range<_Range>; }; + } // namespace __detail + + template<input_range... _Vs> + requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && __detail::__concatable<_Vs...> + class concat_view : public view_interface<concat_view<_Vs...>> + { + tuple<_Vs...> _M_views; + + template<bool _Const> class iterator; + + public: + constexpr concat_view() = default; + + constexpr explicit + concat_view(_Vs... __views) + : _M_views(std::move(__views)...) + { } + + constexpr iterator<false> + begin() requires(!(__detail::__simple_view<_Vs> && ...)) + { + iterator<false> __it(this, in_place_index<0>, ranges::begin(std::get<0>(_M_views))); + __it.template _M_satisfy<0>(); + return __it; + } + + constexpr iterator<true> + begin() const requires (range<const _Vs> && ...) && __detail::__concatable<const _Vs...> + { + iterator<true> __it(this, in_place_index<0>, ranges::begin(std::get<0>(_M_views))); + __it.template _M_satisfy<0>(); + return __it; + } + + constexpr auto + end() requires(!(__detail::__simple_view<_Vs> && ...)) + { + if constexpr (__detail::__last_is_common<_Vs...>::value) + { + constexpr auto __n = sizeof...(_Vs); + return iterator<false>(this, in_place_index<__n - 1>, + ranges::end(std::get<__n - 1>(_M_views))); + } + else + return default_sentinel; + } + + constexpr auto + end() const requires (range<const _Vs> && ...) && __detail::__concatable<const _Vs...> + { + if constexpr (__detail::__last_is_common<const _Vs...>::value) + { + constexpr auto __n = sizeof...(_Vs); + return iterator<true>(this, in_place_index<__n - 1>, + ranges::end(std::get<__n - 1>(_M_views))); + } + else + return default_sentinel; + } + + constexpr auto + size() requires (sized_range<_Vs>&&...) + { + return std::apply([](auto... __sizes) { + using _CT = __detail::__make_unsigned_like_t<common_type_t<decltype(__sizes)...>>; + return (_CT(__sizes) + ...); + }, __detail::__tuple_transform(ranges::size, _M_views)); + } + + constexpr auto + size() const requires (sized_range<const _Vs>&&...) + { + return std::apply([](auto... __sizes) { + using _CT = __detail::__make_unsigned_like_t<common_type_t<decltype(__sizes)...>>; + return (_CT(__sizes) + ...); + }, __detail::__tuple_transform(ranges::size, _M_views)); + } + }; + + template <typename... _Rs> + concat_view(_Rs&&...) -> concat_view<views::all_t<_Rs>...>; + + namespace __detail + { + template<bool _Const, typename... _Vs> + struct __concat_view_iter_cat + { }; + + template<bool _Const, typename... _Vs> + requires __detail::__all_forward<_Const, _Vs...> + struct __concat_view_iter_cat<_Const, _Vs...> + { + static auto + _S_iter_cat() + { + if constexpr (!is_reference_v<__concat_reference_t<__maybe_const_t<_Const, _Vs>...>>) + return input_iterator_tag{}; + else + return []<typename... _Cats>(_Cats... __cats) { + if constexpr ((derived_from<_Cats, random_access_iterator_tag> && ...) + && __concat_is_random_access<_Const, _Vs...>) + return random_access_iterator_tag{}; + else if constexpr ((derived_from<_Cats, bidirectional_iterator_tag> && ...) + && __concat_is_bidirectional<_Const, _Vs...>) + return bidirectional_iterator_tag{}; + else if constexpr ((derived_from<_Cats, forward_iterator_tag> && ...)) + return forward_iterator_tag{}; + else + return input_iterator_tag{}; + }(typename iterator_traits<iterator_t<__maybe_const_t<_Const, _Vs>>> + ::iterator_category{}...); + } + }; + } + + template<input_range... _Vs> + requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) && __detail::__concatable<_Vs...> + template<bool _Const> + class concat_view<_Vs...>::iterator + : public __detail::__concat_view_iter_cat<_Const, _Vs...> + { + static auto + _S_iter_concept() + { + if constexpr (__detail::__concat_is_random_access<_Const, _Vs...>) + return random_access_iterator_tag{}; + else if constexpr (__detail::__concat_is_bidirectional<_Const, _Vs...>) + return bidirectional_iterator_tag{}; + else if constexpr (__detail::__all_forward<_Const, _Vs...>) + return forward_iterator_tag{}; + else + return input_iterator_tag{}; + } + + friend concat_view; + friend iterator<!_Const>; + + public: + // iterator_category defined in __concat_view_iter_cat + using iterator_concept = decltype(_S_iter_concept()); + using value_type = __detail::__concat_value_t<__maybe_const_t<_Const, _Vs>...>; + using difference_type = common_type_t<range_difference_t<__maybe_const_t<_Const, _Vs>>...>; + + private: + using __base_iter = variant<iterator_t<__maybe_const_t<_Const, _Vs>>...>; + + __maybe_const_t<_Const, concat_view>* _M_parent = nullptr; + __base_iter _M_it; + + template<size_t _Nm> + constexpr void + _M_satisfy() + { + if constexpr (_Nm < (sizeof...(_Vs) - 1)) + { + if (std::get<_Nm>(_M_it) == ranges::end(std::get<_Nm>(_M_parent->_M_views))) + { + _M_it.template emplace<_Nm + 1>(ranges::begin + (std::get<_Nm + 1>(_M_parent->_M_views))); + _M_satisfy<_Nm + 1>(); + } + } + } + + template<size_t _Nm> + constexpr void + _M_prev() + { + if constexpr (_Nm == 0) + --std::get<0>(_M_it); + else + { + if (std::get<_Nm>(_M_it) == ranges::begin(std::get<_Nm>(_M_parent->_M_views))) + { + _M_it.template emplace<_Nm - 1>(ranges::end + (std::get<_Nm - 1>(_M_parent->_M_views))); + _M_prev<_Nm - 1>(); + } + else + --std::get<_Nm>(_M_it); + } + } + + template<size_t _Nm> + constexpr void + _M_advance_fwd(difference_type __offset, difference_type __steps) + { + using _Dt = iter_difference_t<variant_alternative_t<_Nm, __base_iter>>; + if constexpr (_Nm == sizeof...(_Vs) - 1) + std::get<_Nm>(_M_it) += static_cast<_Dt>(__steps); + else + { + auto __n_size = ranges::distance(std::get<_Nm>(_M_parent->_M_views)); + if (__offset + __steps < __n_size) + std::get<_Nm>(_M_it) += static_cast<_Dt>(__steps); + else + { + _M_it.template emplace<_Nm + 1>(ranges::begin + (std::get<_Nm + 1>(_M_parent->_M_views))); + _M_advance_fwd<_Nm + 1>(0, __offset + __steps - __n_size); + } + } + } + + template<size_t _Nm> + constexpr void + _M_advance_bwd(difference_type __offset, difference_type __steps) + { + using _Dt = iter_difference_t<variant_alternative_t<_Nm, __base_iter>>; + if constexpr (_Nm == 0) + std::get<_Nm>(_M_it) -= static_cast<_Dt>(__steps); + else { + if (__offset >= __steps) + std::get<_Nm>(_M_it) -= static_cast<_Dt>(__steps); + else + { + auto __prev_size = ranges::distance(std::get<_Nm - 1>(_M_parent->_M_views)); + _M_it.template emplace<_Nm - 1>(ranges::end + (std::get<_Nm - 1>(_M_parent->_M_views))); + _M_advance_bwd<_Nm - 1>(__prev_size, __steps - __offset); + } + } + } + + // Invoke the function object __f, which has a call operator taking a size_t + // index template parameter (bounded by the number of underlying views), + // according the runtime value of __index. + template<typename _Fp> + static constexpr auto + _S_invoke_with_runtime_index(_Fp&& __f, size_t __index) + { + return [&__f, __index]<size_t _Idx>(this auto&& __self) { + if (_Idx == __index) + return __f.template operator()<_Idx>(); + if constexpr (_Idx + 1 < sizeof...(_Vs)) + return __self.template operator()<_Idx + 1>(); + }.template operator()<0>(); + } + + template<typename _Fp> + constexpr auto + _M_invoke_with_runtime_index(_Fp&& __f) + { return _S_invoke_with_runtime_index(std::forward<_Fp>(__f), _M_it.index()); } + + template<typename... _Args> + explicit constexpr + iterator(__maybe_const_t<_Const, concat_view>* __parent, _Args&&... __args) + requires constructible_from<__base_iter, _Args&&...> + : _M_parent(__parent), _M_it(std::forward<_Args>(__args)...) + { } + + public: + iterator() = default; + + constexpr + iterator(iterator<!_Const> __it) + requires _Const && (convertible_to<iterator_t<_Vs>, iterator_t<const _Vs>> && ...) + : _M_parent(__it._M_parent) + { + _M_invoke_with_runtime_index([this, &__it]<size_t _Idx>() { + _M_it.template emplace<_Idx>(std::get<_Idx>(std::move(__it._M_it))); + }); + } + + constexpr decltype(auto) + operator*() const + { + __glibcxx_assert(!_M_it.valueless_by_exception()); + using reference = __detail::__concat_reference_t<__maybe_const_t<_Const, _Vs>...>; + return std::visit([](auto&& __it) -> reference { return *__it; }, _M_it); + } + + constexpr iterator& + operator++() + { + _M_invoke_with_runtime_index([this]<size_t _Idx>() { + ++std::get<_Idx>(_M_it); + _M_satisfy<_Idx>(); + }); + return *this; + } + + constexpr void + operator++(int) + { + ++*this; + } + + constexpr iterator + operator++(int) + requires __detail::__all_forward<_Const, _Vs...> + { + auto __tmp = *this; + ++*this; + return __tmp; + } + + constexpr iterator& + operator--() + requires __detail::__concat_is_bidirectional<_Const, _Vs...> + { + __glibcxx_assert(!_M_it.valueless_by_exception()); + _M_invoke_with_runtime_index([this]<size_t _Idx>() { + _M_prev<_Idx>(); + }); + return *this; + } + + constexpr iterator + operator--(int) + requires __detail::__concat_is_bidirectional<_Const, _Vs...> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + constexpr iterator& + operator+=(difference_type __n) + requires __detail::__concat_is_random_access<_Const, _Vs...> + { + __glibcxx_assert(!_M_it.valueless_by_exception()); + _M_invoke_with_runtime_index([this, __n]<size_t _Idx>() { + auto __begin = ranges::begin(std::get<_Idx>(_M_parent->_M_views)); + if (__n > 0) + _M_advance_fwd<_Idx>(std::get<_Idx>(_M_it) - __begin, __n); + else if (__n < 0) + _M_advance_bwd<_Idx>(std::get<_Idx>(_M_it) - __begin, -__n); + }); + return *this; + } + + constexpr iterator& + operator-=(difference_type __n) + requires __detail::__concat_is_random_access<_Const, _Vs...> + { + *this += -__n; + return *this; + } + + constexpr decltype(auto) + operator[](difference_type __n) const + requires __detail::__concat_is_random_access<_Const, _Vs...> + { return *((*this) + __n); } + + friend constexpr bool + operator==(const iterator& __x, const iterator& __y) + requires (equality_comparable<iterator_t<__maybe_const_t<_Const, _Vs>>> && ...) + { + __glibcxx_assert(!__x._M_it.valueless_by_exception()); + __glibcxx_assert(!__y._M_it.valueless_by_exception()); + return __x._M_it == __y._M_it; + } + + friend constexpr bool + operator==(const iterator& __it, default_sentinel_t) + { + __glibcxx_assert(!__it._M_it.valueless_by_exception()); + constexpr auto __last_idx = sizeof...(_Vs) - 1; + return (__it._M_it.index() == __last_idx + && (std::get<__last_idx>(__it._M_it) + == ranges::end(std::get<__last_idx>(__it._M_parent->_M_views)))); + } + + friend constexpr bool + operator<(const iterator& __x, const iterator& __y) + requires __detail::__all_random_access<_Const, _Vs...> + { return __x._M_it < __y._M_it; } + + friend constexpr bool + operator>(const iterator& __x, const iterator& __y) + requires __detail::__all_random_access<_Const, _Vs...> + { return __x._M_it > __y._M_it; } + + friend constexpr bool + operator<=(const iterator& __x, const iterator& __y) + requires __detail::__all_random_access<_Const, _Vs...> + { return __x._M_it <= __y._M_it; } + + friend constexpr bool + operator>=(const iterator& __x, const iterator& __y) + requires __detail::__all_random_access<_Const, _Vs...> + { return __x._M_it >= __y._M_it; } + + friend constexpr auto + operator<=>(const iterator& __x, const iterator& __y) + requires (__detail::__all_random_access<_Const, _Vs...> + && (three_way_comparable<iterator_t<__maybe_const_t<_Const, _Vs>>> + && ...)) + { return __x._M_it <=> __y._M_it; } + + friend constexpr iterator + operator+(const iterator& __it, difference_type __n) + requires __detail::__concat_is_random_access<_Const, _Vs...> + { return auto(__it) += __n; } + + friend constexpr iterator + operator+(difference_type __n, const iterator& __it) + requires __detail::__concat_is_random_access<_Const, _Vs...> + { return __it + __n; } + + friend constexpr iterator + operator-(const iterator& __it, difference_type __n) + requires __detail::__concat_is_random_access<_Const, _Vs...> + { return auto(__it) -= __n; } + + friend constexpr difference_type + operator-(const iterator& __x, const iterator& __y) + requires __detail::__concat_is_random_access<_Const, _Vs...> + { + return _S_invoke_with_runtime_index([&]<size_t _Ix>() -> difference_type { + return _S_invoke_with_runtime_index([&]<size_t _Iy>() -> difference_type { + if constexpr (_Ix > _Iy) + { + auto __dy = ranges::distance(std::get<_Iy>(__y._M_it), + ranges::end(std::get<_Iy>(__y._M_parent + ->_M_views))); + auto __dx = ranges::distance(ranges::begin(std::get<_Ix>(__x._M_parent + ->_M_views)), + std::get<_Ix>(__x._M_it)); + difference_type __sum = 0; + [&]<size_t _Idx = _Iy + 1>(this auto&& __self) { + if constexpr (_Idx < _Ix) + { + __sum += ranges::size(std::get<_Idx>(__x._M_parent->_M_views)); + __self.template operator()<_Idx + 1>(); + } + }(); + return __dy + __sum + __dx; + } + else if constexpr (_Ix < _Iy) + return -(__y - __x); + else + return std::get<_Ix>(__x._M_it) - std::get<_Iy>(__y._M_it); + }, __y._M_it.index()); + }, __x._M_it.index()); + } + + friend constexpr difference_type + operator-(const iterator& __x, default_sentinel_t) + requires __detail::__concat_is_random_access<_Const, _Vs...> + && __detail::__last_is_common<__maybe_const_t<_Const, _Vs>...>::value + { + return _S_invoke_with_runtime_index([&]<size_t _Ix>() -> difference_type { + auto __dx = ranges::distance(std::get<_Ix>(__x._M_it), + ranges::end(std::get<_Ix>(__x._M_parent->_M_views))); + difference_type __sum = 0; + [&]<size_t _Idx = _Ix + 1>(this auto&& __self) { + if constexpr (_Idx < sizeof...(_Vs)) + { + __sum += ranges::size(std::get<_Idx>(__x._M_parent->_M_views)); + __self.template operator()<_Idx + 1>(); + } + }(); + return -(__dx + __sum); + }, __x._M_it.index()); + } + + friend constexpr difference_type + operator-(default_sentinel_t, const iterator& __x) + requires __detail::__concat_is_random_access<_Const, _Vs...> + && __detail::__last_is_common<__maybe_const_t<_Const, _Vs>...>::value + { return -(__x - default_sentinel); } + + friend constexpr decltype(auto) + iter_move(const iterator& __it) + { + using _Res = __detail::__concat_rvalue_reference_t<__maybe_const_t<_Const, _Vs>...>; + return std::visit([](const auto& __i) -> _Res { + return ranges::iter_move(__i); + }, __it._M_it); + } + + friend constexpr void + iter_swap(const iterator& __x, const iterator& __y) + requires swappable_with<iter_reference_t<iterator>, + iter_reference_t<iterator>> + && (... && indirectly_swappable<iterator_t<__maybe_const_t<_Const, _Vs>>>) + { + std::visit([&](const auto& __it1, const auto& __it2) { + if constexpr (is_same_v<decltype(__it1), decltype(__it2)>) + ranges::iter_swap(__it1, __it2); + else + ranges::swap(*__it1, *__it2); + }, __x._M_it, __y._M_it); + } + }; + + namespace views + { + namespace __detail + { + template<typename... _Ts> + concept __can_concat_view = requires { concat_view(std::declval<_Ts>()...); }; + } + + struct _Concat + { + template<typename... _Ts> + requires __detail::__can_concat_view<_Ts...> + constexpr auto + operator() [[nodiscard]] (_Ts&&... __ts) const + { + if constexpr (sizeof...(_Ts) == 1) + return views::all(std::forward<_Ts>(__ts)...); + else + return concat_view(std::forward<_Ts>(__ts)...); + } + }; + + inline constexpr _Concat concat; + } + +} // namespace ranges +#endif // __cpp_lib_ranges_concat + _GLIBCXX_END_NAMESPACE_VERSION } // namespace std #endif // library concepts diff --git a/libstdc++-v3/testsuite/std/ranges/concat/1.cc b/libstdc++-v3/testsuite/std/ranges/concat/1.cc new file mode 100644 index 00000000000..91c8893cdc5 --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/concat/1.cc @@ -0,0 +1,61 @@ +// { dg-do run { target c++26 } } +// { dg-add-options no_pch } + +#include <ranges> + +#if __cpp_lib_ranges_concat != 202403L +# error "Feature-test macro __cpp_lib_ranges_concat has wrong value in <ranges>" +#endif + +#include <algorithm> +#include <vector> +#include <array> +#include <utility> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> + +namespace ranges = std::ranges; +namespace views = std::views; + +constexpr bool +test01() +{ + std::vector<int> v1{1, 2, 3}, v2{4, 5}, v3{}; + std::array a{6, 7, 8}; + auto s = views::single(9); + + auto v = views::concat(v1, v2, v3, a, s); + VERIFY( ranges::size(v) == 9 ); + VERIFY( ranges::equal(v, views::iota(1, 10)) ); + VERIFY( ranges::equal(v, views::iota(1, 10)) ); + VERIFY( ranges::equal(v | views::reverse, + views::iota(1, 10) | views::reverse) ); + + auto it0 = v.begin(); + auto cit = std::as_const(v).begin(); + VERIFY( it0 == it0 ); + VERIFY( cit == cit ); + VERIFY( it0 == cit ); + VERIFY( it0 - it0 == 0); + for (int i = 0; i < 10; i++) + { + VERIFY( it0 + i - it0 == i); + VERIFY( it0 + i - i + i == it0 + i ); + VERIFY( it0 + i - (it0 + i) == 0 ); + } + VERIFY( std::default_sentinel - it0 == 9 ); + VERIFY( it0 + 9 == std::default_sentinel ); + + auto it5 = it0+5; + ranges::iter_swap(it0, it5); + VERIFY( *it0 == 6 && *it5 == 1 ); + ranges::iter_swap(it0, it5); + *it0 = ranges::iter_move(it0); + return true; +} + +int +main() +{ + static_assert(test01()); +} -- 2.45.0.rc0