diff options
author | Patrick Palka <ppalka@redhat.com> | 2022-08-24 19:17:27 -0400 |
---|---|---|
committer | Patrick Palka <ppalka@redhat.com> | 2022-08-24 19:17:27 -0400 |
commit | 49e25d3e29aa1b56e6e82654de1a452a6cedc265 (patch) | |
tree | 4c13c1fbfeed2a59a2b21b65c7522832d050da4b | |
parent | e5428086c2c8daf69e5916dd5016d1e7b85d3f0d (diff) | |
download | gcc-49e25d3e29aa1b56e6e82654de1a452a6cedc265.zip gcc-49e25d3e29aa1b56e6e82654de1a452a6cedc265.tar.gz gcc-49e25d3e29aa1b56e6e82654de1a452a6cedc265.tar.bz2 |
libstdc++: Implement ranges::zip_view from P2321R2
libstdc++-v3/ChangeLog:
* include/bits/ranges_algo.h (__min_fn, min): Move to ...
* include/bits/ranges_util.h: ... here, in order to avoid
including all of ranges_algo.h from <ranges>.
* include/std/ranges (__detail::__zip_is_common): Define for
C++23 as per P2321R2.
(__detail::__tuple_or_pair): Likewise.
(__detail::__tuple_or_pair_t): Likewise.
(__detail::__tuple_transform): Likewise.
(__detail::__tuple_for_each): Likewise.
(zip_view): Likewise.
(enable_borrowed_range<zip_view>): Likewise.
(__detail::__all_random_access): Likewise.
(__detail::__all_bidirectional): Likewise.
(__detail::__all_forward): Likewise.
(__detail::__zip_view_iter_cat): Likewise.
(zip_view::_Iterator): Likewise.
(zip_view::_Sentinel): Likewise.
* testsuite/std/ranges/zip/1.cc: New test.
-rw-r--r-- | libstdc++-v3/include/bits/ranges_algo.h | 54 | ||||
-rw-r--r-- | libstdc++-v3/include/bits/ranges_util.h | 55 | ||||
-rw-r--r-- | libstdc++-v3/include/std/ranges | 442 | ||||
-rw-r--r-- | libstdc++-v3/testsuite/std/ranges/zip/1.cc | 111 |
4 files changed, 609 insertions, 53 deletions
diff --git a/libstdc++-v3/include/bits/ranges_algo.h b/libstdc++-v3/include/bits/ranges_algo.h index 3d30fb1..2a11636 100644 --- a/libstdc++-v3/include/bits/ranges_algo.h +++ b/libstdc++-v3/include/bits/ranges_algo.h @@ -2902,59 +2902,7 @@ namespace ranges inline constexpr __set_symmetric_difference_fn set_symmetric_difference{}; - struct __min_fn - { - template<typename _Tp, typename _Proj = identity, - indirect_strict_weak_order<projected<const _Tp*, _Proj>> - _Comp = ranges::less> - constexpr const _Tp& - operator()(const _Tp& __a, const _Tp& __b, - _Comp __comp = {}, _Proj __proj = {}) const - { - if (std::__invoke(__comp, - std::__invoke(__proj, __b), - std::__invoke(__proj, __a))) - return __b; - else - return __a; - } - - template<input_range _Range, typename _Proj = identity, - indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>> - _Comp = ranges::less> - requires indirectly_copyable_storable<iterator_t<_Range>, - range_value_t<_Range>*> - constexpr range_value_t<_Range> - operator()(_Range&& __r, _Comp __comp = {}, _Proj __proj = {}) const - { - auto __first = ranges::begin(__r); - auto __last = ranges::end(__r); - __glibcxx_assert(__first != __last); - auto __result = *__first; - while (++__first != __last) - { - auto __tmp = *__first; - if (std::__invoke(__comp, - std::__invoke(__proj, __tmp), - std::__invoke(__proj, __result))) - __result = std::move(__tmp); - } - return __result; - } - - template<copyable _Tp, typename _Proj = identity, - indirect_strict_weak_order<projected<const _Tp*, _Proj>> - _Comp = ranges::less> - constexpr _Tp - operator()(initializer_list<_Tp> __r, - _Comp __comp = {}, _Proj __proj = {}) const - { - return (*this)(ranges::subrange(__r), - std::move(__comp), std::move(__proj)); - } - }; - - inline constexpr __min_fn min{}; + // min is defined in <bits/ranges_util.h>. struct __max_fn { diff --git a/libstdc++-v3/include/bits/ranges_util.h b/libstdc++-v3/include/bits/ranges_util.h index 37d7bc8..bb56dee 100644 --- a/libstdc++-v3/include/bits/ranges_util.h +++ b/libstdc++-v3/include/bits/ranges_util.h @@ -649,6 +649,61 @@ namespace ranges }; inline constexpr __search_fn search{}; + + struct __min_fn + { + template<typename _Tp, typename _Proj = identity, + indirect_strict_weak_order<projected<const _Tp*, _Proj>> + _Comp = ranges::less> + constexpr const _Tp& + operator()(const _Tp& __a, const _Tp& __b, + _Comp __comp = {}, _Proj __proj = {}) const + { + if (std::__invoke(__comp, + std::__invoke(__proj, __b), + std::__invoke(__proj, __a))) + return __b; + else + return __a; + } + + template<input_range _Range, typename _Proj = identity, + indirect_strict_weak_order<projected<iterator_t<_Range>, _Proj>> + _Comp = ranges::less> + requires indirectly_copyable_storable<iterator_t<_Range>, + range_value_t<_Range>*> + constexpr range_value_t<_Range> + operator()(_Range&& __r, _Comp __comp = {}, _Proj __proj = {}) const + { + auto __first = ranges::begin(__r); + auto __last = ranges::end(__r); + __glibcxx_assert(__first != __last); + auto __result = *__first; + while (++__first != __last) + { + auto __tmp = *__first; + if (std::__invoke(__comp, + std::__invoke(__proj, __tmp), + std::__invoke(__proj, __result))) + __result = std::move(__tmp); + } + return __result; + } + + template<copyable _Tp, typename _Proj = identity, + indirect_strict_weak_order<projected<const _Tp*, _Proj>> + _Comp = ranges::less> + constexpr _Tp + operator()(initializer_list<_Tp> __r, + _Comp __comp = {}, _Proj __proj = {}) const + { + return (*this)(ranges::subrange(__r), + std::move(__comp), std::move(__proj)); + } + }; + + inline constexpr __min_fn min{}; + } // namespace ranges using ranges::get; diff --git a/libstdc++-v3/include/std/ranges b/libstdc++-v3/include/std/ranges index 3e71ecb..c600bad6 100644 --- a/libstdc++-v3/include/std/ranges +++ b/libstdc++-v3/include/std/ranges @@ -4352,6 +4352,448 @@ namespace views::__adaptor inline constexpr auto values = elements<1>; } // namespace views +#if __cplusplus > 202002L + namespace __detail + { + template<typename... _Rs> + concept __zip_is_common = (sizeof...(_Rs) == 1 && (common_range<_Rs> && ...)) + || (!(bidirectional_range<_Rs> && ...) && (common_range<_Rs> && ...)) + || ((random_access_range<_Rs> && ...) && (sized_range<_Rs> && ...)); + + template<typename... _Ts> + struct __tuple_or_pair + { using type = std::tuple<_Ts...>; }; + + template<typename _Tp, typename _Up> + struct __tuple_or_pair<_Tp, _Up> + { using type = pair<_Tp, _Up>; }; + + template<typename... _Ts> + using __tuple_or_pair_t = typename __tuple_or_pair<_Ts...>::type; + + template<typename _Fp, typename _Tuple> + constexpr auto + __tuple_transform(_Fp&& __f, _Tuple&& __tuple) + { + return std::apply([&]<typename... _Ts>(_Ts&&... __elts) { + return __tuple_or_pair_t<invoke_result_t<_Fp&, _Ts>...> + (std::__invoke(__f, std::forward<_Ts>(__elts))...); + }, std::forward<_Tuple>(__tuple)); + } + + template<typename _Fp, typename _Tuple> + constexpr void + __tuple_for_each(_Fp&& __f, _Tuple&& __tuple) + { + std::apply([&]<typename... _Ts>(_Ts&&... __elts) { + (std::__invoke(__f, std::forward<_Ts>(__elts)), ...); + }, std::forward<_Tuple>(__tuple)); + } + } // namespace __detail + + template<input_range... _Vs> + requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) + class zip_view : public view_interface<zip_view<_Vs...>> + { + tuple<_Vs...> _M_views; + + template<bool> class _Iterator; + template<bool> class _Sentinel; + + public: + zip_view() = default; + + constexpr explicit + zip_view(_Vs... __views) + : _M_views(std::move(__views)...) + { } + + constexpr auto + begin() requires (!(__detail::__simple_view<_Vs> && ...)) + { return _Iterator<false>(__detail::__tuple_transform(ranges::begin, _M_views)); } + + constexpr auto + begin() const requires (range<const _Vs> && ...) + { return _Iterator<true>(__detail::__tuple_transform(ranges::begin, _M_views)); } + + constexpr auto + end() requires (!(__detail::__simple_view<_Vs> && ...)) + { + if constexpr (!__detail::__zip_is_common<_Vs...>) + return _Sentinel<false>(__detail::__tuple_transform(ranges::end, _M_views)); + else if constexpr ((random_access_range<_Vs> && ...)) + return begin() + iter_difference_t<_Iterator<false>>(size()); + else + return _Iterator<false>(__detail::__tuple_transform(ranges::end, _M_views)); + } + + constexpr auto + end() const requires (range<const _Vs> && ...) + { + if constexpr (!__detail::__zip_is_common<const _Vs...>) + return _Sentinel<true>(__detail::__tuple_transform(ranges::end, _M_views)); + else if constexpr ((random_access_range<const _Vs> && ...)) + return begin() + iter_difference_t<_Iterator<true>>(size()); + else + return _Iterator<true>(__detail::__tuple_transform(ranges::end, _M_views)); + } + + 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 ranges::min({_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 ranges::min({_CT(sizes)...}); + }, __detail::__tuple_transform(ranges::size, _M_views)); + } + }; + + template<typename... _Rs> + zip_view(_Rs&&...) -> zip_view<views::all_t<_Rs>...>; + + template<typename... _Views> + inline constexpr bool enable_borrowed_range<zip_view<_Views...>> + = (enable_borrowed_range<_Views> && ...); + + namespace __detail + { + template<bool _Const, typename... _Vs> + concept __all_random_access + = (random_access_range<__maybe_const_t<_Const, _Vs>> && ...); + + template<bool _Const, typename... _Vs> + concept __all_bidirectional + = (bidirectional_range<__maybe_const_t<_Const, _Vs>> && ...); + + template<bool _Const, typename... _Vs> + concept __all_forward + = (forward_range<__maybe_const_t<_Const, _Vs>> && ...); + + template<bool _Const, typename... _Views> + struct __zip_view_iter_cat + { }; + + template<bool _Const, typename... _Views> + requires __all_forward<_Const, _Views...> + struct __zip_view_iter_cat<_Const, _Views...> + { using iterator_category = input_iterator_tag; }; + } // namespace __detail + + template<input_range... _Vs> + requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) + template<bool _Const> + class zip_view<_Vs...>::_Iterator + : public __detail::__zip_view_iter_cat<_Const, _Vs...> + { + __detail::__tuple_or_pair_t<iterator_t<__detail::__maybe_const_t<_Const, _Vs>>...> _M_current; + + constexpr explicit + _Iterator(decltype(_M_current) __current) + : _M_current(std::move(__current)) + { } + + static auto + _S_iter_concept() + { + if constexpr (__detail::__all_random_access<_Const, _Vs...>) + return random_access_iterator_tag{}; + else if constexpr (__detail::__all_bidirectional<_Const, _Vs...>) + return bidirectional_iterator_tag{}; + else if constexpr (__detail::__all_forward<_Const, _Vs...>) + return forward_iterator_tag{}; + else + return input_iterator_tag{}; + } + + public: + // iterator_category defined in __zip_view_iter_cat + using iterator_concept = decltype(_S_iter_concept()); + using value_type + = __detail::__tuple_or_pair_t<range_value_t<__detail::__maybe_const_t<_Const, _Vs>>...>; + using difference_type + = common_type_t<range_difference_t<__detail::__maybe_const_t<_Const, _Vs>>...>; + + _Iterator() = default; + + constexpr + _Iterator(_Iterator<!_Const> __i) + requires _Const + && (convertible_to<iterator_t<_Vs>, + iterator_t<__detail::__maybe_const_t<_Const, _Vs>>> && ...) + : _M_current(std::move(__i._M_current)) + { } + + constexpr auto + operator*() const + { + auto __f = [](auto& __i) -> decltype(auto) { + return *__i; + }; + return __detail::__tuple_transform(__f, _M_current); + } + + constexpr _Iterator& + operator++() + { + __detail::__tuple_for_each([](auto& __i) { ++__i; }, _M_current); + 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::__all_bidirectional<_Const, _Vs...> + { + __detail::__tuple_for_each([](auto& __i) { --__i; }, _M_current); + return *this; + } + + constexpr _Iterator + operator--(int) + requires __detail::__all_bidirectional<_Const, _Vs...> + { + auto __tmp = *this; + --*this; + return __tmp; + } + + constexpr _Iterator& + operator+=(difference_type __x) + requires __detail::__all_random_access<_Const, _Vs...> + { + auto __f = [&]<typename _It>(_It& __i) { + __i += iter_difference_t<_It>(__x); + }; + __detail::__tuple_for_each(__f, _M_current); + return *this; + } + + constexpr _Iterator& + operator-=(difference_type __x) + requires __detail::__all_random_access<_Const, _Vs...> + { + auto __f = [&]<typename _It>(_It& __i) { + __i -= iter_difference_t<_It>(__x); + }; + __detail::__tuple_for_each(__f, _M_current); + return *this; + } + + constexpr auto + operator[](difference_type __n) const + requires __detail::__all_random_access<_Const, _Vs...> + { + auto __f = [&]<typename _It>(_It& __i) -> decltype(auto) { + return __i[iter_difference_t<_It>(__n)]; + }; + return __detail::__tuple_transform(__f, _M_current); + } + + friend constexpr bool + operator==(const _Iterator& __x, const _Iterator& __y) + requires (equality_comparable<iterator_t<__detail::__maybe_const_t<_Const, _Vs>>> && ...) + { + if constexpr (__detail::__all_bidirectional<_Const, _Vs...>) + return __x._M_current == __y._M_current; + else + return [&]<size_t... _Is>(index_sequence<_Is...>) { + return ((std::get<_Is>(__x._M_current) == std::get<_Is>(__y._M_current)) || ...); + }(make_index_sequence<sizeof...(_Vs)>{}); + } + + friend constexpr bool + operator<(const _Iterator& __x, const _Iterator& __y) + requires __detail::__all_random_access<_Const, _Vs...> + { return __x._M_current < __y._M_current; } + + friend constexpr bool + operator>(const _Iterator& __x, const _Iterator& __y) + requires __detail::__all_random_access<_Const, _Vs...> + { return __y < __x; } + + friend constexpr bool + operator<=(const _Iterator& __x, const _Iterator& __y) + requires __detail::__all_random_access<_Const, _Vs...> + { return !(__y < __x); } + + friend constexpr bool + operator>=(const _Iterator& __x, const _Iterator& __y) + requires __detail::__all_random_access<_Const, _Vs...> + { return !(__x < __y); } + + friend constexpr auto + operator<=>(const _Iterator& __x, const _Iterator& __y) + requires __detail::__all_random_access<_Const, _Vs...> + && (three_way_comparable<iterator_t<__detail::__maybe_const_t<_Const, _Vs>>> && ...) + { return __x._M_current <=> __y._M_current; } + + friend constexpr _Iterator + operator+(const _Iterator& __i, difference_type __n) + requires __detail::__all_random_access<_Const, _Vs...> + { + auto __r = __i; + __r += __n; + return __r; + } + + friend constexpr _Iterator + operator+(difference_type __n, const _Iterator& __i) + requires __detail::__all_random_access<_Const, _Vs...> + { + auto __r = __i; + __r += __n; + return __r; + } + + friend constexpr _Iterator + operator-(const _Iterator& __i, difference_type __n) + requires __detail::__all_random_access<_Const, _Vs...> + { + auto __r = __i; + __r -= __n; + return __r; + } + + friend constexpr difference_type + operator-(const _Iterator& __x, const _Iterator& __y) + requires (sized_sentinel_for<iterator_t<__detail::__maybe_const_t<_Const, _Vs>>, + iterator_t<__detail::__maybe_const_t<_Const, _Vs>>> && ...) + { + return [&]<size_t... _Is>(index_sequence<_Is...>) { + return ranges::min({difference_type(std::get<_Is>(__x._M_current) + - std::get<_Is>(__y._M_current))...}, + ranges::less{}, + [](difference_type __i) -> make_unsigned_t<difference_type> { + return __i < 0 ? -__i : __i; + }); + }(make_index_sequence<sizeof...(_Vs)>{}); + } + + friend constexpr auto + iter_move(const _Iterator& __i) + { return __detail::__tuple_transform(ranges::iter_move, __i._M_current); } + + friend constexpr void + iter_swap(const _Iterator& __l, const _Iterator& __r) + requires (indirectly_swappable<iterator_t<__detail::__maybe_const_t<_Const, _Vs>>> && ...) + { + [&]<size_t... _Is>(index_sequence<_Is...>) { + (ranges::iter_swap(std::get<_Is>(__l._M_current), std::get<_Is>(__r._M_current)), ...); + }(make_index_sequence<sizeof...(_Vs)>{}); + } + + friend class zip_view; + }; + + template<input_range... _Vs> + requires (view<_Vs> && ...) && (sizeof...(_Vs) > 0) + template<bool _Const> + class zip_view<_Vs...>::_Sentinel + { + __detail::__tuple_or_pair_t<sentinel_t<__detail::__maybe_const_t<_Const, _Vs>>...> _M_end; + + constexpr explicit + _Sentinel(decltype(_M_end) __end) + : _M_end(__end) + { } + + friend class zip_view; + + public: + _Sentinel() = default; + + constexpr + _Sentinel(_Sentinel<!_Const> __i) + requires _Const + && (convertible_to<sentinel_t<_Vs>, + sentinel_t<__detail::__maybe_const_t<_Const, _Vs>>> && ...) + : _M_end(std::move(__i._M_end)) + { } + + template<bool _OtherConst> + requires (sentinel_for<sentinel_t<__detail::__maybe_const_t<_Const, _Vs>>, + iterator_t<__detail::__maybe_const_t<_OtherConst, _Vs>>> && ...) + friend constexpr bool + operator==(const _Iterator<_OtherConst>& __x, const _Sentinel& __y) + { + return [&]<size_t... _Is>(index_sequence<_Is...>) { + return ((std::get<_Is>(__x._M_current) == std::get<_Is>(__y._M_end)) || ...); + }(make_index_sequence<sizeof...(_Vs)>{}); + } + + template<bool _OtherConst> + requires (sized_sentinel_for<sentinel_t<__detail::__maybe_const_t<_Const, _Vs>>, + iterator_t<__detail::__maybe_const_t<_OtherConst, _Vs>>> && ...) + friend constexpr auto + operator-(const _Iterator<_OtherConst>& __x, const _Sentinel& __y) + { + using _Ret + = common_type_t<range_difference_t<__detail::__maybe_const_t<_OtherConst, _Vs>>...>; + return [&]<size_t... _Is>(index_sequence<_Is...>) { + return ranges::min({_Ret(std::get<_Is>(__x._M_current) - std::get<_Is>(__y._M_end))...}, + ranges::less{}, + [](_Ret __i) -> make_unsigned_t<_Ret> { + return __i < 0 ? -__i : __i; + }); + }(make_index_sequence<sizeof...(_Vs)>{}); + } + + template<bool _OtherConst> + requires (sized_sentinel_for<sentinel_t<__detail::__maybe_const_t<_Const, _Vs>>, + iterator_t<__detail::__maybe_const_t<_OtherConst, _Vs>>> && ...) + friend constexpr auto + operator-(const _Sentinel& __y, const _Iterator<_OtherConst>& __x) + { return -(__x - __y); } + }; + + namespace views + { + namespace __detail + { + template<typename... _Ts> + concept __can_zip_view + = requires { zip_view<all_t<_Ts>...>(std::declval<_Ts>()...); }; + } + + struct _Zip + { + template<typename... _Ts> + requires (sizeof...(_Ts) == 0 || __detail::__can_zip_view<_Ts...>) + [[nodiscard]] + constexpr auto + operator()(_Ts&&... __ts) const + { + if constexpr (sizeof...(_Ts) == 0) + return views::empty<tuple<>>; + else + return zip_view<all_t<_Ts>...>(std::forward<_Ts>(__ts)...); + } + }; + + inline constexpr _Zip zip; + } +#endif // C++23 } // namespace ranges namespace views = ranges::views; diff --git a/libstdc++-v3/testsuite/std/ranges/zip/1.cc b/libstdc++-v3/testsuite/std/ranges/zip/1.cc new file mode 100644 index 0000000..0113efd --- /dev/null +++ b/libstdc++-v3/testsuite/std/ranges/zip/1.cc @@ -0,0 +1,111 @@ +// { dg-options "-std=gnu++23" } +// { dg-do run { target c++23 } } + +#include <ranges> +#include <algorithm> +#include <utility> +#include <vector> +#include <testsuite_hooks.h> +#include <testsuite_iterators.h> + +namespace ranges = std::ranges; +namespace views = std::views; + +constexpr bool +test01() +{ + static_assert(ranges::empty(views::zip())); + static_assert(ranges::empty(views::empty<int>)); + + auto z1 = views::zip(std::array{1, 2}); + const auto i0 = z1.begin(), i1 = z1.begin() + 1; + VERIFY( i0 + 1 - 1 == i0 ); + VERIFY( i0 < i1 ); + VERIFY( i1 < z1.end() ); + VERIFY( i1 - i0 == 1 ); + VERIFY( i0 - i1 == -1 ); + VERIFY( z1.end() - i1 == 1 ); + VERIFY( i1 - z1.end() == -1 ); + ranges::iter_swap(i0, i1); + VERIFY( ranges::equal(std::move(z1) | views::keys, (int[]){2, 1}) ); + + auto z2 = views::zip(std::array{1, 2}, std::array{3, 4, 5}); + auto i2 = z2.begin(); + i2 += 1; + i2 -= -1; + VERIFY( i2 == z2.end() ); + VERIFY( ranges::size(z2) == 2 ); + VERIFY( ranges::size(std::as_const(z2)) == 2 ); + VERIFY( z2[0].first == 1 && z2[0].second == 3 ); + VERIFY( z2[1].first == 2 && z2[1].second == 4 ); + for (const auto [x, y] : z2) + { + VERIFY( y - x == 2 ); + std::swap(x, y); + } + + int x[2] = {1, 2}, y[2] = {3, 4}, z[2] = {5, 6}; + const auto z3 = views::zip(x, y, z); + VERIFY( ranges::size(z3) == 2 ); + for (int i = 0; i < ranges::size(x); i++) + { + VERIFY( &std::get<0>(z3[i]) == &x[i] ); + VERIFY( &std::get<1>(z3[i]) == &y[i] ); + VERIFY( &std::get<2>(z3[i]) == &z[i] ); + } + + return true; +} + +constexpr bool +test02() +{ + using __gnu_test::test_input_range; + using __gnu_test::test_forward_range; + using __gnu_test::test_random_access_range; + + using ty1 = ranges::zip_view<views::all_t<test_forward_range<int>>, + views::all_t<test_random_access_range<int>>>; + static_assert(ranges::forward_range<ty1>); + static_assert(!ranges::random_access_range<ty1>); + static_assert(!ranges::sized_range<ty1>); + + using ty2 = ranges::zip_view<views::all_t<test_forward_range<int>>, + views::all_t<test_input_range<int>>, + views::all_t<test_forward_range<int>>>; + static_assert(ranges::input_range<ty2>); + static_assert(!ranges::forward_range<ty2>); + static_assert(!ranges::sized_range<ty2>); + + return true; +} + +constexpr bool +test03() +{ + int u[] = {1, 2, 3, 4}, v[] = {4, 5, 6}, w[] = {7, 8, 9, 10}; + auto z = views::zip(u | views::filter([](auto) { return true; }), v, w); + using ty = decltype(z); + static_assert(ranges::forward_range<ty>); + static_assert(!ranges::common_range<ty>); + static_assert(!ranges::sized_range<ty>); + VERIFY( z.begin() == z.begin() ); + VERIFY( z.begin() != z.end() ); + VERIFY( ranges::next(z.begin(), 3) == z.end() ); + auto it = z.begin(); + ++it; + it++; + it--; + --it; + VERIFY( it == z.begin() ); + + return true; +} + +int +main() +{ + static_assert(test01()); + static_assert(test02()); + static_assert(test03()); +} |