[libc++] add zip_view and views::zip for C++23

- add zip_view and views::zip for C++23
- added unit tests
- implemented section 5.6 (zip) in P2321R2

I used clang-format to format the files but they look nothing like the rest of the code base. Manually indenting each line to match the styles sounds like an impossible task. Is there any clang-format file which can format it reasonable similar to the rest of the code base so that I can manually format the rest lines that look weird?

Reviewed By: ldionne, #libc, philnik, var-const

Spies: Mordante, philnik, libcxx-commits, mgorny

Differential Revision: https://reviews.llvm.org/D122806
This commit is contained in:
Hui Xie 2022-04-25 12:18:54 +02:00 committed by Nikolas Klauser
parent 04f78947e4
commit 042dc3c46d
34 changed files with 4162 additions and 8 deletions

View File

@ -7,10 +7,10 @@ Section,Description,Dependencies,Assignee,Complete
| `[allocator.uses.construction] <https://wg21.link/allocator.uses.construction>`_", "[pair] uses_allocator_construction_args overloads", None, Unassigned, |Not Started| | `[allocator.uses.construction] <https://wg21.link/allocator.uses.construction>`_", "[pair] uses_allocator_construction_args overloads", None, Unassigned, |Not Started|
| `[vector.bool] <https://wg21.link/vector.bool>`_, "[vector<bool>::reference] add const operator= overload", None, Nikolas Klauser, |Not Started| | `[vector.bool] <https://wg21.link/vector.bool>`_, "[vector<bool>::reference] add const operator= overload", None, Nikolas Klauser, |Not Started|
| `[iterator.concept.winc] <https://wg21.link/iterator.concept.winc>`_, "Update weakly_comparable", None, Unassigned, |Not Started| | `[iterator.concept.winc] <https://wg21.link/iterator.concept.winc>`_, "Update weakly_comparable", None, Unassigned, |Not Started|
| `[range.zip] <https://wg21.link/ranges.syn>`_, "zip_view", "| `zip_view::iterator` | `[range.zip] <https://wg21.link/ranges.syn>`_, "`zip_view <https://reviews.llvm.org/D122806>`_", "| `zip_view::iterator`
| `zip_view::sentinel`", Unassigned, |Not Started| | `zip_view::sentinel`", Hui Xie, |Complete|
| `[range.zip.iterator] <https://wg21.link/range.zip.transform>`_, "zip_view::iterator", None, Unassigned, |Not Started| | `[range.zip.iterator] <https://wg21.link/range.zip.iterator>`_, "`zip_view::iterator <https://reviews.llvm.org/D122806>`_", None, Hui Xie, |Complete|
| `[range.zip.sentinel] <https://wg21.link/range.zip.sentinel>`_, "zip_view::sentinel", None, Unassigned, |Not Started| | `[range.zip.sentinel] <https://wg21.link/range.zip.sentinel>`_, "`zip_view::sentinel <https://reviews.llvm.org/D122806>`_", None, Hui Xie, |Complete|
| `[range.zip.transform.view] <https://wg21.link/range.zip.transform.view>`_, "zip_transform_view", "| `zip_transform_view::iterator` | `[range.zip.transform.view] <https://wg21.link/range.zip.transform.view>`_, "zip_transform_view", "| `zip_transform_view::iterator`
| `zip_transform_view::sentinel`", Unassigned, |Not Started| | `zip_transform_view::sentinel`", Unassigned, |Not Started|
| `[range.zip.transform.iterator] <https://wg21.link/range.zip.transform.iterator>`_, "zip_transform_view::iterator", None, Unassigned, |Not Started| | `[range.zip.transform.iterator] <https://wg21.link/range.zip.transform.iterator>`_, "zip_transform_view::iterator", None, Unassigned, |Not Started|

1 Section Description Dependencies Assignee Complete
7 | `[vector.bool] <https://wg21.link/vector.bool>`_ [vector<bool>::reference] add const operator= overload None Nikolas Klauser |Not Started|
8 | `[iterator.concept.winc] <https://wg21.link/iterator.concept.winc>`_ Update weakly_comparable None Unassigned |Not Started|
9 | `[range.zip] <https://wg21.link/ranges.syn>`_ zip_view `zip_view <https://reviews.llvm.org/D122806>`_ | `zip_view::iterator` | `zip_view::sentinel` Unassigned Hui Xie |Not Started| |Complete|
10 | `[range.zip.iterator] <https://wg21.link/range.zip.transform>`_ | `[range.zip.iterator] <https://wg21.link/range.zip.iterator>`_ zip_view::iterator `zip_view::iterator <https://reviews.llvm.org/D122806>`_ None Unassigned Hui Xie |Not Started| |Complete|
11 | `[range.zip.sentinel] <https://wg21.link/range.zip.sentinel>`_ zip_view::sentinel `zip_view::sentinel <https://reviews.llvm.org/D122806>`_ None Unassigned Hui Xie |Not Started| |Complete|
12 | `[range.zip.transform.view] <https://wg21.link/range.zip.transform.view>`_ zip_transform_view | `zip_transform_view::iterator` | `zip_transform_view::sentinel` Unassigned |Not Started|
13 | `[range.zip.transform.iterator] <https://wg21.link/range.zip.transform.iterator>`_ zip_transform_view::iterator None Unassigned |Not Started|
14 | `[range.zip.transform.sentinel] <https://wg21.link/range.zip.transform.sentinel>`_ zip_transform_view::sentinel None Unassigned |Not Started|
15 | `[range.adjacent.view] <https://wg21.link/range.adjacent.view>`_ adjacent_view | `adjacent_view::iterator` | `adjacent_view::sentinel` Unassigned |Not Started|
16 | `[range.adjacent.iterator] <https://wg21.link/range.adjacent.iterator>`_ adjacent_view::iterator None Unassigned |Not Started|

View File

@ -396,6 +396,7 @@ set(files
__ranges/transform_view.h __ranges/transform_view.h
__ranges/view_interface.h __ranges/view_interface.h
__ranges/views.h __ranges/views.h
__ranges/zip_view.h
__split_buffer __split_buffer
__std_stream __std_stream
__string __string

View File

@ -0,0 +1,511 @@
// -*- C++ -*-
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef _LIBCPP___RANGES_ZIP_VIEW_H
#define _LIBCPP___RANGES_ZIP_VIEW_H
#include <__config>
#include <__algorithm/ranges_min.h>
#include <__compare/three_way_comparable.h>
#include <__concepts/convertible_to.h>
#include <__concepts/equality_comparable.h>
#include <__functional/invoke.h>
#include <__functional/operations.h>
#include <__iterator/concepts.h>
#include <__iterator/incrementable_traits.h>
#include <__iterator/iter_move.h>
#include <__iterator/iter_swap.h>
#include <__iterator/iterator_traits.h>
#include <__ranges/access.h>
#include <__ranges/all.h>
#include <__ranges/concepts.h>
#include <__ranges/empty_view.h>
#include <__ranges/enable_borrowed_range.h>
#include <__ranges/size.h>
#include <__ranges/view_interface.h>
#include <__utility/forward.h>
#include <__utility/integer_sequence.h>
#include <__utility/move.h>
#include <tuple>
#include <type_traits>
#if !defined(_LIBCPP_HAS_NO_PRAGMA_SYSTEM_HEADER)
# pragma GCC system_header
#endif
_LIBCPP_PUSH_MACROS
#include <__undef_macros>
_LIBCPP_BEGIN_NAMESPACE_STD
#if _LIBCPP_STD_VER > 20 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
namespace ranges {
template <class... _Ranges>
concept __zip_is_common = (sizeof...(_Ranges) == 1 && (common_range<_Ranges> && ...)) ||
(!(bidirectional_range<_Ranges> && ...) && (common_range<_Ranges> && ...)) ||
((random_access_range<_Ranges> && ...) && (sized_range<_Ranges> && ...));
template <typename _Tp, typename _Up>
auto __tuple_or_pair_test() -> pair<_Tp, _Up>;
template <typename... _Types>
requires(sizeof...(_Types) != 2)
auto __tuple_or_pair_test() -> tuple<_Types...>;
template <class... _Types>
using __tuple_or_pair = decltype(__tuple_or_pair_test<_Types...>());
template <class _Fun, class _Tuple>
_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_transform(_Fun&& __f, _Tuple&& __tuple) {
return std::apply(
[&]<class... _Types>(_Types&&... __elements) {
return __tuple_or_pair<invoke_result_t<_Fun&, _Types>...>(
std::invoke(__f, std::forward<_Types>(__elements))...);
},
std::forward<_Tuple>(__tuple));
}
template <class _Fun, class _Tuple>
_LIBCPP_HIDE_FROM_ABI constexpr void __tuple_for_each(_Fun&& __f, _Tuple&& __tuple) {
std::apply(
[&]<class... _Types>(_Types&&... __elements) { (std::invoke(__f, std::forward<_Types>(__elements)), ...); },
std::forward<_Tuple>(__tuple));
}
template <class _Fun, class _Tuple1, class _Tuple2, size_t... _Indices>
_LIBCPP_HIDE_FROM_ABI constexpr __tuple_or_pair<
invoke_result_t<_Fun&, typename tuple_element<_Indices, remove_cvref_t<_Tuple1>>::type,
typename tuple_element<_Indices, remove_cvref_t<_Tuple2>>::type>...>
__tuple_zip_transform(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2, index_sequence<_Indices...>) {
return {std::invoke(__f, std::get<_Indices>(std::forward<_Tuple1>(__tuple1)),
std::get<_Indices>(std::forward<_Tuple2>(__tuple2)))...};
}
template <class _Fun, class _Tuple1, class _Tuple2>
_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_zip_transform(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2) {
return ranges::__tuple_zip_transform(__f, std::forward<_Tuple1>(__tuple1), std::forward<_Tuple2>(__tuple2),
std::make_index_sequence<tuple_size<remove_cvref_t<_Tuple1>>::value>());
}
template <class _Fun, class _Tuple1, class _Tuple2, size_t... _Indices>
_LIBCPP_HIDE_FROM_ABI constexpr void __tuple_zip_for_each(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2,
index_sequence<_Indices...>) {
(std::invoke(__f, std::get<_Indices>(std::forward<_Tuple1>(__tuple1)),
std::get<_Indices>(std::forward<_Tuple2>(__tuple2))),
...);
}
template <class _Fun, class _Tuple1, class _Tuple2>
_LIBCPP_HIDE_FROM_ABI constexpr auto __tuple_zip_for_each(_Fun&& __f, _Tuple1&& __tuple1, _Tuple2&& __tuple2) {
return ranges::__tuple_zip_for_each(__f, std::forward<_Tuple1>(__tuple1), std::forward<_Tuple2>(__tuple2),
std::make_index_sequence<tuple_size<remove_cvref_t<_Tuple1>>::value>());
}
template <class _Tuple1, class _Tuple2>
_LIBCPP_HIDE_FROM_ABI constexpr bool __tuple_any_equals(const _Tuple1& __tuple1, const _Tuple2& __tuple2) {
const auto __equals = ranges::__tuple_zip_transform(std::equal_to<>(), __tuple1, __tuple2);
return std::apply([](auto... __bools) { return (__bools || ...); }, __equals);
}
// abs in cstdlib is not constexpr
// TODO : remove __abs once P0533R9 is implemented.
template <class _Tp>
_LIBCPP_HIDE_FROM_ABI constexpr _Tp __abs(_Tp __t) {
return __t < 0 ? -__t : __t;
}
template <input_range... _Views>
requires(view<_Views> && ...) && (sizeof...(_Views) > 0)
class zip_view : public view_interface<zip_view<_Views...>> {
_LIBCPP_NO_UNIQUE_ADDRESS tuple<_Views...> __views_;
template <bool>
class __iterator;
template <bool>
class __sentinel;
public:
_LIBCPP_HIDE_FROM_ABI
zip_view() = default;
_LIBCPP_HIDE_FROM_ABI
constexpr explicit zip_view(_Views... __views) : __views_(std::move(__views)...) {}
_LIBCPP_HIDE_FROM_ABI
constexpr auto begin()
requires(!(__simple_view<_Views> && ...)) {
return __iterator<false>(ranges::__tuple_transform(ranges::begin, __views_));
}
_LIBCPP_HIDE_FROM_ABI
constexpr auto begin() const
requires(range<const _Views> && ...) {
return __iterator<true>(ranges::__tuple_transform(ranges::begin, __views_));
}
_LIBCPP_HIDE_FROM_ABI
constexpr auto end()
requires(!(__simple_view<_Views> && ...)) {
if constexpr (!__zip_is_common<_Views...>) {
return __sentinel<false>(ranges::__tuple_transform(ranges::end, __views_));
} else if constexpr ((random_access_range<_Views> && ...)) {
return begin() + iter_difference_t<__iterator<false>>(size());
} else {
return __iterator<false>(ranges::__tuple_transform(ranges::end, __views_));
}
}
_LIBCPP_HIDE_FROM_ABI
constexpr auto end() const
requires(range<const _Views> && ...) {
if constexpr (!__zip_is_common<const _Views...>) {
return __sentinel<true>(ranges::__tuple_transform(ranges::end, __views_));
} else if constexpr ((random_access_range<const _Views> && ...)) {
return begin() + iter_difference_t<__iterator<true>>(size());
} else {
return __iterator<true>(ranges::__tuple_transform(ranges::end, __views_));
}
}
_LIBCPP_HIDE_FROM_ABI
constexpr auto size()
requires(sized_range<_Views> && ...) {
return std::apply(
[](auto... __sizes) {
using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
return ranges::min({_CT(__sizes)...});
},
ranges::__tuple_transform(ranges::size, __views_));
}
_LIBCPP_HIDE_FROM_ABI
constexpr auto size() const
requires(sized_range<const _Views> && ...) {
return std::apply(
[](auto... __sizes) {
using _CT = make_unsigned_t<common_type_t<decltype(__sizes)...>>;
return ranges::min({_CT(__sizes)...});
},
ranges::__tuple_transform(ranges::size, __views_));
}
};
template <class... _Ranges>
zip_view(_Ranges&&...) -> zip_view<views::all_t<_Ranges>...>;
template <bool _Const, class... _Views>
concept __zip_all_random_access = (random_access_range<__maybe_const<_Const, _Views>> && ...);
template <bool _Const, class... _Views>
concept __zip_all_bidirectional = (bidirectional_range<__maybe_const<_Const, _Views>> && ...);
template <bool _Const, class... _Views>
concept __zip_all_forward = (forward_range<__maybe_const<_Const, _Views>> && ...);
template <bool _Const, class... _Views>
consteval auto __get_zip_view_iterator_tag() {
if constexpr (__zip_all_random_access<_Const, _Views...>) {
return random_access_iterator_tag();
} else if constexpr (__zip_all_bidirectional<_Const, _Views...>) {
return bidirectional_iterator_tag();
} else if constexpr (__zip_all_forward<_Const, _Views...>) {
return forward_iterator_tag();
} else {
return input_iterator_tag();
}
}
template <bool _Const, class... _Views>
struct __zip_view_iterator_category_base {};
template <bool _Const, class... _Views>
requires __zip_all_forward<_Const, _Views...>
struct __zip_view_iterator_category_base<_Const, _Views...> {
using iterator_category = input_iterator_tag;
};
template <input_range... _Views>
requires(view<_Views> && ...) && (sizeof...(_Views) > 0)
template <bool _Const>
class zip_view<_Views...>::__iterator : public __zip_view_iterator_category_base<_Const, _Views...> {
__tuple_or_pair<iterator_t<__maybe_const<_Const, _Views>>...> __current_;
_LIBCPP_HIDE_FROM_ABI
constexpr explicit __iterator(__tuple_or_pair<iterator_t<__maybe_const<_Const, _Views>>...> __current)
: __current_(std::move(__current)) {}
template <bool>
friend class zip_view<_Views...>::__iterator;
template <bool>
friend class zip_view<_Views...>::__sentinel;
friend class zip_view<_Views...>;
public:
using iterator_concept = decltype(__get_zip_view_iterator_tag<_Const, _Views...>());
using value_type = __tuple_or_pair<range_value_t<__maybe_const<_Const, _Views>>...>;
using difference_type = common_type_t<range_difference_t<__maybe_const<_Const, _Views>>...>;
_LIBCPP_HIDE_FROM_ABI
__iterator() = default;
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator(__iterator<!_Const> __i)
requires _Const && (convertible_to<iterator_t<_Views>, iterator_t<__maybe_const<_Const, _Views>>> && ...)
: __current_(std::move(__i.__current_)) {}
_LIBCPP_HIDE_FROM_ABI
constexpr auto operator*() const {
return ranges::__tuple_transform([](auto& __i) -> decltype(auto) { return *__i; }, __current_);
}
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator& operator++() {
ranges::__tuple_for_each([](auto& __i) { ++__i; }, __current_);
return *this;
}
_LIBCPP_HIDE_FROM_ABI
constexpr void operator++(int) { ++*this; }
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator operator++(int)
requires __zip_all_forward<_Const, _Views...> {
auto __tmp = *this;
++*this;
return __tmp;
}
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator& operator--()
requires __zip_all_bidirectional<_Const, _Views...> {
ranges::__tuple_for_each([](auto& __i) { --__i; }, __current_);
return *this;
}
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator operator--(int)
requires __zip_all_bidirectional<_Const, _Views...> {
auto __tmp = *this;
--*this;
return __tmp;
}
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator& operator+=(difference_type __x)
requires __zip_all_random_access<_Const, _Views...> {
ranges::__tuple_for_each([&]<class _Iter>(_Iter& __i) { __i += iter_difference_t<_Iter>(__x); }, __current_);
return *this;
}
_LIBCPP_HIDE_FROM_ABI
constexpr __iterator& operator-=(difference_type __x)
requires __zip_all_random_access<_Const, _Views...> {
ranges::__tuple_for_each([&]<class _Iter>(_Iter& __i) { __i -= iter_difference_t<_Iter>(__x); }, __current_);
return *this;
}
_LIBCPP_HIDE_FROM_ABI
constexpr auto operator[](difference_type __n) const
requires __zip_all_random_access<_Const, _Views...> {
return ranges::__tuple_transform(
[&]<class _Iter>(_Iter& __i) -> decltype(auto) { return __i[iter_difference_t<_Iter>(__n)]; }, __current_);
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr bool operator==(const __iterator& __x, const __iterator& __y)
requires(equality_comparable<iterator_t<__maybe_const<_Const, _Views>>> && ...) {
if constexpr (__zip_all_bidirectional<_Const, _Views...>) {
return __x.__current_ == __y.__current_;
} else {
return ranges::__tuple_any_equals(__x.__current_, __y.__current_);
}
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr bool operator<(const __iterator& __x, const __iterator& __y)
requires __zip_all_random_access<_Const, _Views...> {
return __x.__current_ < __y.__current_;
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr bool operator>(const __iterator& __x, const __iterator& __y)
requires __zip_all_random_access<_Const, _Views...> {
return __y < __x;
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr bool operator<=(const __iterator& __x, const __iterator& __y)
requires __zip_all_random_access<_Const, _Views...> {
return !(__y < __x);
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr bool operator>=(const __iterator& __x, const __iterator& __y)
requires __zip_all_random_access<_Const, _Views...> {
return !(__x < __y);
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr auto operator<=>(const __iterator& __x, const __iterator& __y)
requires __zip_all_random_access<_Const, _Views...> &&
(three_way_comparable<iterator_t<__maybe_const<_Const, _Views>>> && ...) {
return __x.__current_ <=> __y.__current_;
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr __iterator operator+(const __iterator& __i, difference_type __n)
requires __zip_all_random_access<_Const, _Views...> {
auto __r = __i;
__r += __n;
return __r;
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr __iterator operator+(difference_type __n, const __iterator& __i)
requires __zip_all_random_access<_Const, _Views...> {
return __i + __n;
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr __iterator operator-(const __iterator& __i, difference_type __n)
requires __zip_all_random_access<_Const, _Views...> {
auto __r = __i;
__r -= __n;
return __r;
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr difference_type operator-(const __iterator& __x, const __iterator& __y)
requires(sized_sentinel_for<iterator_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_Const, _Views>>> &&
...) {
const auto __diffs = ranges::__tuple_zip_transform(minus<>(), __x.__current_, __y.__current_);
return std::apply(
[](auto... __ds) {
return ranges::min({difference_type(__ds)...},
[](auto __a, auto __b) { return ranges::__abs(__a) < ranges::__abs(__b); });
},
__diffs);
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr auto iter_move(const __iterator& __i) noexcept(
(noexcept(ranges::iter_move(declval<const iterator_t<__maybe_const<_Const, _Views>>&>())) && ...) &&
(is_nothrow_move_constructible_v<range_rvalue_reference_t<__maybe_const<_Const, _Views>>> && ...)) {
return ranges::__tuple_transform(ranges::iter_move, __i.__current_);
}
_LIBCPP_HIDE_FROM_ABI
friend constexpr void iter_swap(const __iterator& __l, const __iterator& __r) noexcept(
(noexcept(ranges::iter_swap(declval<const iterator_t<__maybe_const<_Const, _Views>>&>(),
declval<const iterator_t<__maybe_const<_Const, _Views>>&>())) &&
...))
requires(indirectly_swappable<iterator_t<__maybe_const<_Const, _Views>>> && ...) {
ranges::__tuple_zip_for_each(ranges::iter_swap, __l.__current_, __r.__current_);
}
};
template <input_range... _Views>
requires(view<_Views> && ...) && (sizeof...(_Views) > 0)
template <bool _Const>
class zip_view<_Views...>::__sentinel {
__tuple_or_pair<sentinel_t<__maybe_const<_Const, _Views>>...> __end_;
_LIBCPP_HIDE_FROM_ABI
constexpr explicit __sentinel(__tuple_or_pair<sentinel_t<__maybe_const<_Const, _Views>>...> __end) : __end_(__end) {}
friend class zip_view<_Views...>;
// hidden friend cannot access private member of iterator because they are friends of friends
template <bool _OtherConst>
_LIBCPP_HIDE_FROM_ABI static constexpr decltype(auto)
__iter_current(zip_view<_Views...>::__iterator<_OtherConst> const& __it) {
return (__it.__current_);
}
public:
_LIBCPP_HIDE_FROM_ABI
__sentinel() = default;
_LIBCPP_HIDE_FROM_ABI
constexpr __sentinel(__sentinel<!_Const> __i)
requires _Const && (convertible_to<sentinel_t<_Views>, sentinel_t<__maybe_const<_Const, _Views>>> && ...)
: __end_(std::move(__i.__end_)) {}
template <bool _OtherConst>
requires(sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> &&
...)
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
return ranges::__tuple_any_equals(__iter_current(__x), __y.__end_);
}
template <bool _OtherConst>
requires(
sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> &&
...)
_LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t<range_difference_t<__maybe_const<_OtherConst, _Views>>...>
operator-(const __iterator<_OtherConst>& __x, const __sentinel& __y) {
const auto __diffs = ranges::__tuple_zip_transform(minus<>(), __iter_current(__x), __y.__end_);
return std::apply(
[](auto... __ds) {
using _Diff = common_type_t<range_difference_t<__maybe_const<_OtherConst, _Views>>...>;
return ranges::min({_Diff(__ds)...},
[](auto __a, auto __b) { return ranges::__abs(__a) < ranges::__abs(__b); });
},
__diffs);
}
template <bool _OtherConst>
requires(
sized_sentinel_for<sentinel_t<__maybe_const<_Const, _Views>>, iterator_t<__maybe_const<_OtherConst, _Views>>> &&
...)
_LIBCPP_HIDE_FROM_ABI friend constexpr common_type_t<range_difference_t<__maybe_const<_OtherConst, _Views>>...>
operator-(const __sentinel& __y, const __iterator<_OtherConst>& __x) {
return -(__x - __y);
}
};
template <class... _Views>
inline constexpr bool enable_borrowed_range<zip_view<_Views...>> = (enable_borrowed_range<_Views> && ...);
namespace views {
namespace __zip {
struct __fn {
_LIBCPP_HIDE_FROM_ABI constexpr auto operator()() const noexcept { return empty_view<tuple<>>{}; }
template <class... _Ranges>
_LIBCPP_HIDE_FROM_ABI constexpr auto operator()(_Ranges&&... rs) const
noexcept(noexcept(zip_view<all_t<_Ranges&&>...>(std::forward<_Ranges>(rs)...)))
-> decltype(zip_view<all_t<_Ranges&&>...>(std::forward<_Ranges>(rs)...)) {
return zip_view<all_t<_Ranges>...>(std::forward<_Ranges>(rs)...);
}
};
} // namespace __zip
inline namespace __cpo {
inline constexpr auto zip = __zip::__fn{};
} // namespace __cpo
} // namespace views
} // namespace ranges
#endif // _LIBCPP_STD_VER > 20 && !defined(_LIBCPP_HAS_NO_INCOMPLETE_RANGES)
_LIBCPP_END_NAMESPACE_STD
_LIBCPP_POP_MACROS
#endif // _LIBCPP___RANGES_ZIP_VIEW_H

View File

@ -878,6 +878,7 @@ module std [system] {
} }
module view_interface { private header "__ranges/view_interface.h" } module view_interface { private header "__ranges/view_interface.h" }
module views { private header "__ranges/views.h" } module views { private header "__ranges/views.h" }
module zip_view { private header "__ranges/zip_view.h" }
} }
} }
module ratio { module ratio {

View File

@ -227,6 +227,17 @@ namespace std::ranges {
namespace views { namespace views {
inline constexpr unspecified lazy_split = unspecified; inline constexpr unspecified lazy_split = unspecified;
} }
// [range.zip], zip view
template<input_range... Views>
requires (view<Views> && ...) && (sizeof...(Views) > 0)
class zip_view; // C++2b
template<class... Views>
inline constexpr bool enable_borrowed_range<zip_view<Views...>> = // C++2b
(enable_borrowed_range<Views> && ...);
namespace views { inline constexpr unspecified zip = unspecified; } // C++2b
} }
namespace std { namespace std {
@ -290,6 +301,7 @@ namespace std {
#include <__ranges/transform_view.h> #include <__ranges/transform_view.h>
#include <__ranges/view_interface.h> #include <__ranges/view_interface.h>
#include <__ranges/views.h> #include <__ranges/views.h>
#include <__ranges/zip_view.h>
#include <__tuple> // TODO: <ranges> has to export std::tuple_size. Replace this, once <tuple> is granularized. #include <__tuple> // TODO: <ranges> has to export std::tuple_size. Replace this, once <tuple> is granularized.
#include <compare> // Required by the standard. #include <compare> // Required by the standard.
#include <initializer_list> // Required by the standard. #include <initializer_list> // Required by the standard.

View File

@ -427,6 +427,7 @@ END-SCRIPT
#include <__ranges/transform_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/transform_view.h'}} #include <__ranges/transform_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/transform_view.h'}}
#include <__ranges/view_interface.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/view_interface.h'}} #include <__ranges/view_interface.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/view_interface.h'}}
#include <__ranges/views.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/views.h'}} #include <__ranges/views.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/views.h'}}
#include <__ranges/zip_view.h> // expected-error@*:* {{use of private header from outside its module: '__ranges/zip_view.h'}}
#include <__split_buffer> // expected-error@*:* {{use of private header from outside its module: '__split_buffer'}} #include <__split_buffer> // expected-error@*:* {{use of private header from outside its module: '__split_buffer'}}
#include <__std_stream> // expected-error@*:* {{use of private header from outside its module: '__std_stream'}} #include <__std_stream> // expected-error@*:* {{use of private header from outside its module: '__std_stream'}}
#include <__string> // expected-error@*:* {{use of private header from outside its module: '__string'}} #include <__string> // expected-error@*:* {{use of private header from outside its module: '__string'}}

View File

@ -34,8 +34,8 @@ struct DefaultConstructibleFunction {
constexpr int operator()(int i) const { return i + state_; } constexpr int operator()(int i) const { return i + state_; }
}; };
struct NoDefaultView : std::ranges::view_base { struct NoDefaultCtrView : std::ranges::view_base {
NoDefaultView() = delete; NoDefaultCtrView() = delete;
int* begin() const; int* begin() const;
int* end() const; int* end() const;
}; };
@ -62,9 +62,9 @@ constexpr bool test() {
assert(view[2] == 103); assert(view[2] == 103);
} }
static_assert(!std::is_default_constructible_v<std::ranges::transform_view<NoDefaultView, DefaultConstructibleFunction>>); static_assert(!std::is_default_constructible_v<std::ranges::transform_view<NoDefaultCtrView, DefaultConstructibleFunction>>);
static_assert(!std::is_default_constructible_v<std::ranges::transform_view<DefaultConstructibleView, NoDefaultFunction>>); static_assert(!std::is_default_constructible_v<std::ranges::transform_view<DefaultConstructibleView, NoDefaultFunction>>);
static_assert(!std::is_default_constructible_v<std::ranges::transform_view<NoDefaultView, NoDefaultFunction>>); static_assert(!std::is_default_constructible_v<std::ranges::transform_view<NoDefaultCtrView, NoDefaultFunction>>);
return true; return true;
} }

View File

@ -0,0 +1,109 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr auto begin() requires (!(simple-view<Views> && ...));
// constexpr auto begin() const requires (range<const Views> && ...);
#include <ranges>
#include <cassert>
#include <concepts>
#include <tuple>
#include <utility>
#include "types.h"
template <class T>
concept HasConstBegin = requires(const T& ct) { ct.begin(); };
template <class T>
concept HasBegin = requires(T& t) { t.begin(); };
template <class T>
concept HasConstAndNonConstBegin =
HasConstBegin<T> &&
requires(T& t, const T& ct) { requires !std::same_as<decltype(t.begin()), decltype(ct.begin())>; };
template <class T>
concept HasOnlyNonConstBegin = HasBegin<T> && !
HasConstBegin<T>;
template <class T>
concept HasOnlyConstBegin = HasConstBegin<T> && !
HasConstAndNonConstBegin<T>;
struct NoConstBeginView : std::ranges::view_base {
int* begin();
int* end();
};
constexpr bool test() {
int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
{
// all underlying iterators should be at the begin position
std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0), std::ranges::single_view(2.));
std::same_as<std::tuple<int&, int, double&>> decltype(auto) val = *v.begin();
assert(val == std::make_tuple(1, 0, 2.0));
assert(&(std::get<0>(val)) == &buffer[0]);
}
{
// with empty range
std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::ranges::empty_view<int>());
assert(v.begin() == v.end());
}
{
// underlying ranges all model simple-view
std::ranges::zip_view v(SimpleCommon{buffer}, SimpleCommon{buffer});
static_assert(std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
assert(v.begin() == std::as_const(v).begin());
auto [x, y] = *std::as_const(v).begin();
assert(&x == &buffer[0]);
assert(&y == &buffer[0]);
using View = decltype(v);
static_assert(HasOnlyConstBegin<View>);
static_assert(!HasOnlyNonConstBegin<View>);
static_assert(!HasConstAndNonConstBegin<View>);
}
{
// not all underlying ranges model simple-view
std::ranges::zip_view v(SimpleCommon{buffer}, NonSimpleNonCommon{buffer});
static_assert(!std::is_same_v<decltype(v.begin()), decltype(std::as_const(v).begin())>);
assert(v.begin() == std::as_const(v).begin());
auto [x, y] = *std::as_const(v).begin();
assert(&x == &buffer[0]);
assert(&y == &buffer[0]);
using View = decltype(v);
static_assert(!HasOnlyConstBegin<View>);
static_assert(!HasOnlyNonConstBegin<View>);
static_assert(HasConstAndNonConstBegin<View>);
}
{
// underlying const R is not a range
using View = std::ranges::zip_view<SimpleCommon, NoConstBeginView>;
static_assert(!HasOnlyConstBegin<View>);
static_assert(HasOnlyNonConstBegin<View>);
static_assert(!HasConstAndNonConstBegin<View>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,40 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// template<class... Views>
// inline constexpr bool enable_borrowed_range<zip_view<Views...>> =
// (enable_borrowed_range<Views> && ...);
#include <ranges>
#include <tuple>
struct Borrowed : std::ranges::view_base {
int* begin() const;
int* end() const;
};
template <>
inline constexpr bool std::ranges::enable_borrowed_range<Borrowed> = true;
static_assert(std::ranges::borrowed_range<Borrowed>);
struct NonBorrowed : std::ranges::view_base {
int* begin() const;
int* end() const;
};
static_assert(!std::ranges::borrowed_range<NonBorrowed>);
// test borrowed_range
static_assert(std::ranges::borrowed_range<std::ranges::zip_view<Borrowed>>);
static_assert(std::ranges::borrowed_range<std::ranges::zip_view<Borrowed, Borrowed>>);
static_assert(!std::ranges::borrowed_range<std::ranges::zip_view<Borrowed, NonBorrowed>>);
static_assert(!std::ranges::borrowed_range<std::ranges::zip_view<NonBorrowed>>);
static_assert(!std::ranges::borrowed_range<std::ranges::zip_view<NonBorrowed, NonBorrowed>>);

View File

@ -0,0 +1,77 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// std::views::zip
#include <ranges>
#include <array>
#include <cassert>
#include <tuple>
#include <type_traits>
#include <utility>
#include "types.h"
static_assert(std::is_invocable_v<decltype((std::views::zip))>);
static_assert(!std::is_invocable_v<decltype((std::views::zip)), int>);
static_assert(std::is_invocable_v<decltype((std::views::zip)), SizedRandomAccessView>);
static_assert(
std::is_invocable_v<decltype((std::views::zip)), SizedRandomAccessView, std::ranges::iota_view<int, int>>);
static_assert(!std::is_invocable_v<decltype((std::views::zip)), SizedRandomAccessView, int>);
constexpr bool test() {
{
// zip zero arguments
auto v = std::views::zip();
assert(std::ranges::empty(v));
static_assert(std::is_same_v<decltype(v), std::ranges::empty_view<std::tuple<>>>);
}
{
// zip a view
int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
std::same_as<std::ranges::zip_view<SizedRandomAccessView>> decltype(auto) v =
std::views::zip(SizedRandomAccessView{buffer});
assert(std::ranges::size(v) == 8);
static_assert(std::is_same_v<std::ranges::range_reference_t<decltype(v)>, std::tuple<int&>>);
}
{
// zip a viewable range
std::array a{1, 2, 3};
std::same_as<std::ranges::zip_view<std::ranges::ref_view<std::array<int, 3>>>> decltype(auto) v =
std::views::zip(a);
assert(&(std::get<0>(*v.begin())) == &(a[0]));
static_assert(std::is_same_v<std::ranges::range_reference_t<decltype(v)>, std::tuple<int&>>);
}
{
// zip the zip_view
int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
std::same_as<std::ranges::zip_view<SizedRandomAccessView, SizedRandomAccessView>> decltype(auto) v =
std::views::zip(SizedRandomAccessView{buffer}, SizedRandomAccessView{buffer});
std::same_as<
std::ranges::zip_view<std::ranges::zip_view<SizedRandomAccessView, SizedRandomAccessView>>> decltype(auto) v2 =
std::views::zip(v);
static_assert(std::is_same_v<std::ranges::range_reference_t<decltype(v2)>, std::tuple<std::pair<int&, int&>>>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,40 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// template <class... Rs>
// zip_view(Rs&&...) -> zip_view<views::all_t<Rs>...>;
#include <cassert>
#include <ranges>
#include <utility>
struct Container {
int* begin() const;
int* end() const;
};
struct View : std::ranges::view_base {
int* begin() const;
int* end() const;
};
void testCTAD() {
static_assert(std::is_same_v<decltype(std::ranges::zip_view(Container{})),
std::ranges::zip_view<std::ranges::owning_view<Container>>>);
static_assert(std::is_same_v<decltype(std::ranges::zip_view(Container{}, View{})),
std::ranges::zip_view<std::ranges::owning_view<Container>, View>>);
Container c{};
static_assert(std::is_same_v<
decltype(std::ranges::zip_view(Container{}, View{}, c)),
std::ranges::zip_view<std::ranges::owning_view<Container>, View, std::ranges::ref_view<Container>>>);
}

View File

@ -0,0 +1,68 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// zip_view() = default;
#include <ranges>
#include <cassert>
#include <type_traits>
#include <utility>
constexpr int buff[] = {1, 2, 3};
struct DefaultConstructibleView : std::ranges::view_base {
constexpr DefaultConstructibleView() : begin_(buff), end_(buff + 3) {}
constexpr int const* begin() const { return begin_; }
constexpr int const* end() const { return end_; }
private:
int const* begin_;
int const* end_;
};
struct NoDefaultCtrView : std::ranges::view_base {
NoDefaultCtrView() = delete;
int* begin() const;
int* end() const;
};
// The default constructor requires all underlying views to be default constructible.
// It is implicitly required by the tuple's constructor. If any of the iterators are
// not default constructible, zip iterator's =default would be implicitly deleted.
static_assert(std::is_default_constructible_v<std::ranges::zip_view<DefaultConstructibleView>>);
static_assert(
std::is_default_constructible_v<std::ranges::zip_view<DefaultConstructibleView, DefaultConstructibleView>>);
static_assert(!std::is_default_constructible_v<std::ranges::zip_view<DefaultConstructibleView, NoDefaultCtrView>>);
static_assert(!std::is_default_constructible_v<std::ranges::zip_view<NoDefaultCtrView, NoDefaultCtrView>>);
static_assert(!std::is_default_constructible_v<std::ranges::zip_view<NoDefaultCtrView>>);
constexpr bool test() {
{
using View = std::ranges::zip_view<DefaultConstructibleView, DefaultConstructibleView>;
View v = View(); // the default constructor is not explicit
assert(v.size() == 3);
auto it = v.begin();
using Pair = std::pair<const int&, const int&>;
assert(*it++ == Pair(buff[0], buff[0]));
assert(*it++ == Pair(buff[1], buff[1]));
assert(*it == Pair(buff[2], buff[2]));
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,100 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr explicit zip_view(Views...)
#include <ranges>
#include <tuple>
#include "types.h"
template <class T>
void conversion_test(T);
template <class T, class... Args>
concept implicitly_constructible_from = requires(Args&&... args) { conversion_test<T>({std::move(args)...}); };
// test constructor is explicit
static_assert(std::constructible_from<std::ranges::zip_view<SimpleCommon>, SimpleCommon>);
static_assert(!implicitly_constructible_from<std::ranges::zip_view<SimpleCommon>, SimpleCommon>);
static_assert(std::constructible_from<std::ranges::zip_view<SimpleCommon, SimpleCommon>, SimpleCommon, SimpleCommon>);
static_assert(
!implicitly_constructible_from<std::ranges::zip_view<SimpleCommon, SimpleCommon>, SimpleCommon, SimpleCommon>);
struct MoveAwareView : std::ranges::view_base {
int moves = 0;
constexpr MoveAwareView() = default;
constexpr MoveAwareView(MoveAwareView&& other) : moves(other.moves + 1) { other.moves = 1; }
constexpr MoveAwareView& operator=(MoveAwareView&& other) {
moves = other.moves + 1;
other.moves = 0;
return *this;
}
constexpr const int* begin() const { return &moves; }
constexpr const int* end() const { return &moves + 1; }
};
template <class View1, class View2>
constexpr void constructorTest(auto&& buffer1, auto&& buffer2) {
std::ranges::zip_view v{View1{buffer1}, View2{buffer2}};
auto [i, j] = *v.begin();
assert(i == buffer1[0]);
assert(j == buffer2[0]);
};
constexpr bool test() {
int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
int buffer2[4] = {9, 8, 7, 6};
{
// constructor from views
std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0), std::ranges::single_view(2.));
auto [i, j, k] = *v.begin();
assert(i == 1);
assert(j == 0);
assert(k == 2.0);
}
{
// arguments are moved once
MoveAwareView mv;
std::ranges::zip_view v{std::move(mv), MoveAwareView{}};
auto [numMoves1, numMoves2] = *v.begin();
assert(numMoves1 == 2); // one move from the local variable to parameter, one move from parameter to member
assert(numMoves2 == 1);
}
// input and forward
{
constructorTest<InputCommonView, ForwardSizedView>(buffer, buffer2);
}
// bidi and random_access
{
constructorTest<BidiCommonView, SizedRandomAccessView>(buffer, buffer2);
}
// contiguous
{
constructorTest<ContiguousCommonView, ContiguousCommonView>(buffer, buffer2);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,396 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr auto end() requires(!(simple-view<Views> && ...))
// constexpr auto end() const requires(range<const Views>&&...)
#include <ranges>
#include <tuple>
#include "types.h"
// ID | simple | common | bidi | random | sized | #views | v.end() | as_const(v)
// | | | | access | | | | .end()
// ---|--------|--------|------|--------|-------|--------|----------------|---------------
// 1 | Y | Y | Y | Y | Y | 1 | iterator<true> | iterator<true>
// 2 | Y | Y | Y | Y | Y | >1 | iterator<true> | iterator<true>
// 3 | Y | N | Y | Y | N | 1 | sentinel<true> | sentinel<true>
// 4 | Y | N | Y | Y | N | >1 | sentinel<true> | sentinel<true>
// 5 | Y | Y | Y | N | Y | 1 | iterator<true> | iterator<true>
// 6 | Y | Y | Y | N | Y | >1 | sentinel<true> | sentinel<true>
// 7 | Y | Y | Y | N | N | 1 | iterator<true> | iterator<true>
// 8 | Y | Y | Y | N | N | >1 | sentinel<true> | sentinel<true>
// 9 | Y | Y | N | N | Y | 1 | iterator<true> | iterator<true>
// 10 | Y | Y | N | N | Y | >1 | iterator<true> | iterator<true>
// 11 | Y | Y | N | N | N | 1 | iterator<true> | iterator<true>
// 12 | Y | Y | N | N | N | >1 | iterator<true> | iterator<true>
// 13 | Y | N | Y | Y | Y | 1 | iterator<true> | iterator<true>
// 14 | Y | N | Y | Y | Y | >1 | iterator<true> | iterator<true>
// 15 | Y | N | Y | N | Y | 1 | sentinel<true> | sentinel<true>
// 16 | Y | N | Y | N | Y | >1 | sentinel<true> | sentinel<true>
// 17 | Y | N | Y | N | N | 1 | sentinel<true> | sentinel<true>
// 18 | Y | N | Y | N | N | >1 | sentinel<true> | sentinel<true>
// 19 | Y | N | N | N | Y | 1 | sentinel<true> | sentinel<true>
// 20 | Y | N | N | N | Y | >1 | sentinel<true> | sentinel<true>
// 21 | Y | N | N | N | N | 1 | sentinel<true> | sentinel<true>
// 22 | Y | N | N | N | N | >1 | sentinel<true> | sentinel<true>
// 23 | N | Y | Y | Y | Y | 1 | iterator<false>| iterator<true>
// 24 | N | Y | Y | Y | Y | >1 | iterator<false>| iterator<true>
// 25 | N | N | Y | Y | N | 1 | sentinel<false>| sentinel<true>
// 26 | N | N | Y | Y | N | >1 | sentinel<false>| sentinel<true>
// 27 | N | Y | Y | N | Y | 1 | iterator<false>| iterator<true>
// 28 | N | Y | Y | N | Y | >1 | sentinel<false>| sentinel<true>
// 29 | N | Y | Y | N | N | 1 | iterator<false>| iterator<true>
// 30 | N | Y | Y | N | N | >1 | sentinel<false>| sentinel<true>
// 31 | N | Y | N | N | Y | 1 | iterator<false>| iterator<true>
// 32 | N | Y | N | N | Y | >1 | iterator<false>| iterator<true>
// 33 | N | Y | N | N | N | 1 | iterator<false>| iterator<true>
// 34 | N | Y | N | N | N | >1 | iterator<false>| iterator<true>
// 35 | N | N | Y | Y | Y | 1 | iterator<false>| iterator<true>
// 36 | N | N | Y | Y | Y | >1 | iterator<false>| iterator<true>
// 37 | N | N | Y | N | Y | 1 | sentinel<false>| sentinel<true>
// 38 | N | N | Y | N | Y | >1 | sentinel<false>| sentinel<true>
// 39 | N | N | Y | N | N | 1 | sentinel<false>| sentinel<true>
// 40 | N | N | Y | N | N | >1 | sentinel<false>| sentinel<true>
// 41 | N | N | N | N | Y | 1 | sentinel<false>| sentinel<true>
// 42 | N | N | N | N | Y | >1 | sentinel<false>| sentinel<true>
// 43 | N | N | N | N | N | 1 | sentinel<false>| sentinel<true>
// 44 | N | N | N | N | N | >1 | sentinel<false>| sentinel<true>
constexpr bool test() {
int buffer1[5] = {1, 2, 3, 4, 5};
int buffer2[1] = {1};
int buffer3[3] = {1, 2, 3};
{
// test ID 1
std::ranges::zip_view v{SimpleCommonRandomAccessSized(buffer1)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(v.begin() + 5 == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 2
std::ranges::zip_view v{SimpleCommonRandomAccessSized(buffer1), SimpleCommonRandomAccessSized(buffer2)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(v.begin() + 1 == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 3
std::ranges::zip_view v{NonSizedRandomAccessView(buffer1)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(v.begin() + 5 == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 4
std::ranges::zip_view v{NonSizedRandomAccessView(buffer1), NonSizedRandomAccessView(buffer3)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(v.begin() + 3 == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 5
std::ranges::zip_view v{SizedBidiCommon(buffer1)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(std::next(v.begin(), 5) == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 6
std::ranges::zip_view v{SizedBidiCommon(buffer1), SizedBidiCommon(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 7
std::ranges::zip_view v{BidiCommonView(buffer1)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(std::next(v.begin(), 5) == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 8
std::ranges::zip_view v{BidiCommonView(buffer1), BidiCommonView(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 9
std::ranges::zip_view v{ForwardSizedView(buffer1)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(std::next(v.begin(), 5) == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 10
std::ranges::zip_view v{ForwardSizedView(buffer1), ForwardSizedView(buffer2)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 11
std::ranges::zip_view v{InputCommonView(buffer1)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(std::ranges::next(v.begin(), 5) == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 12
std::ranges::zip_view v{InputCommonView(buffer1), InputCommonView(buffer2)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 13
std::ranges::zip_view v{SimpleNonCommonRandomAcessSized(buffer1)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(v.begin() + 5 == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 14
std::ranges::zip_view v{SimpleNonCommonRandomAcessSized(buffer1), SimpleNonCommonRandomAcessSized(buffer2)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(v.begin() + 1 == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 15
std::ranges::zip_view v{SizedBidiNonCommonView(buffer1)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(std::next(v.begin(), 5) == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 16
std::ranges::zip_view v{SizedBidiNonCommonView(buffer1), SizedBidiNonCommonView(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 17
std::ranges::zip_view v{BidiNonCommonView(buffer1)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(std::next(v.begin(), 5) == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 18
std::ranges::zip_view v{BidiNonCommonView(buffer1), BidiNonCommonView(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 19
std::ranges::zip_view v{ForwardSizedNonCommon(buffer1)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(std::next(v.begin(), 5) == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 20
std::ranges::zip_view v{ForwardSizedNonCommon(buffer1), ForwardSizedNonCommon(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 21
std::ranges::zip_view v{InputNonCommonView(buffer1)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(std::ranges::next(v.begin(), 5) == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 22
std::ranges::zip_view v{InputNonCommonView(buffer1), InputNonCommonView(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 23
std::ranges::zip_view v{NonSimpleCommonRandomAccessSized(buffer1)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(v.begin() + 5 == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 24
std::ranges::zip_view v{NonSimpleCommonRandomAccessSized(buffer1), NonSimpleCommonRandomAccessSized(buffer2)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(v.begin() + 1 == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 25
std::ranges::zip_view v{NonSimpleNonSizedRandomAccessView(buffer1)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(v.begin() + 5 == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 26
std::ranges::zip_view v{NonSimpleNonSizedRandomAccessView(buffer1), NonSimpleNonSizedRandomAccessView(buffer3)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(v.begin() + 3 == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 27
std::ranges::zip_view v{NonSimpleSizedBidiCommon(buffer1)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(std::next(v.begin(), 5) == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 28
std::ranges::zip_view v{NonSimpleSizedBidiCommon(buffer1), NonSimpleSizedBidiCommon(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 29
std::ranges::zip_view v{NonSimpleBidiCommonView(buffer1)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(std::next(v.begin(), 5) == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 30
std::ranges::zip_view v{NonSimpleBidiCommonView(buffer1), NonSimpleBidiCommonView(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 31
std::ranges::zip_view v{NonSimpleForwardSizedView(buffer1)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(std::next(v.begin(), 5) == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 32
std::ranges::zip_view v{NonSimpleForwardSizedView(buffer1), NonSimpleForwardSizedView(buffer2)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 33
std::ranges::zip_view v{NonSimpleInputCommonView(buffer1)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(std::ranges::next(v.begin(), 5) == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 34
std::ranges::zip_view v{NonSimpleInputCommonView(buffer1), NonSimpleInputCommonView(buffer2)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 35
std::ranges::zip_view v{NonSimpleNonCommonRandomAcessSized(buffer1)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(v.begin() + 5 == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 36
std::ranges::zip_view v{NonSimpleNonCommonRandomAcessSized(buffer1), NonSimpleNonCommonRandomAcessSized(buffer2)};
static_assert(std::ranges::common_range<decltype(v)>);
assert(v.begin() + 1 == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 37
std::ranges::zip_view v{NonSimpleSizedBidiNonCommonView(buffer1)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(std::next(v.begin(), 5) == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 38
std::ranges::zip_view v{NonSimpleSizedBidiNonCommonView(buffer1), NonSimpleSizedBidiNonCommonView(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 39
std::ranges::zip_view v{NonSimpleBidiNonCommonView(buffer1)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(std::next(v.begin(), 5) == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 40
std::ranges::zip_view v{NonSimpleBidiNonCommonView(buffer1), NonSimpleBidiNonCommonView(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 41
std::ranges::zip_view v{NonSimpleForwardSizedNonCommon(buffer1)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(std::next(v.begin(), 5) == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 42
std::ranges::zip_view v{NonSimpleForwardSizedNonCommon(buffer1), NonSimpleForwardSizedNonCommon(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 43
std::ranges::zip_view v{NonSimpleInputNonCommonView(buffer1)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(std::ranges::next(v.begin(), 5) == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// test ID 44
std::ranges::zip_view v{NonSimpleInputNonCommonView(buffer1), NonSimpleInputNonCommonView(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
assert(++v.begin() == v.end());
static_assert(!std::is_same_v<decltype(v.end()), decltype(std::as_const(v).end())>);
}
{
// end should go to the minimum length when zip is common and random_access sized
std::ranges::zip_view v(std::views::iota(0, 4), std::views::iota(0, 8));
auto it = --(v.end());
auto [x, y] = *it;
assert(x == 3);
assert(y == 3); // y should not go to the end "7"
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,55 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// Some basic examples of how zip_view might be used in the wild. This is a general
// collection of sample algorithms and functions that try to mock general usage of
// this view.
#include <ranges>
#include <array>
#include <cassert>
#include <vector>
#include <string>
int main(int, char**) {
{
std::ranges::zip_view v{
std::array{1, 2},
std::vector{4, 5, 6},
std::array{7},
};
assert(std::ranges::size(v) == 1);
assert(*v.begin() == std::make_tuple(1, 4, 7));
}
{
using namespace std::string_literals;
std::vector v{1, 2, 3, 4};
std::array a{"abc"s, "def"s, "gh"s};
auto view = std::views::zip(v, a);
auto it = view.begin();
assert(&(std::get<0>(*it)) == &(v[0]));
assert(&(std::get<1>(*it)) == &(a[0]));
++it;
assert(&(std::get<0>(*it)) == &(v[1]));
assert(&(std::get<1>(*it)) == &(a[1]));
++it;
assert(&(std::get<0>(*it)) == &(v[2]));
assert(&(std::get<1>(*it)) == &(a[2]));
++it;
assert(it == view.end());
}
return 0;
}

View File

@ -0,0 +1,143 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// x += n;
// x + n;
// n + x;
// x -= n;
// x - n;
// x - y;
// All the arithmetic operators have the constraint `requires all-random-access<Const, Views...>;`,
// except `operator-(x, y)` which instead has the constraint
// `requires (sized_­sentinel_­for<iterator_t<maybe-const<Const, Views>>,
// iterator_t<maybe-const<Const, Views>>> && ...);`
#include <ranges>
#include <array>
#include <concepts>
#include <functional>
#include "../types.h"
template <class T, class U>
concept canPlusEqual = requires(T& t, U& u) { t += u; };
template <class T, class U>
concept canMinusEqual = requires(T& t, U& u) { t -= u; };
constexpr bool test() {
int buffer1[5] = {1, 2, 3, 4, 5};
int buffer2[9] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
SizedRandomAccessView a{buffer1};
static_assert(std::ranges::random_access_range<decltype(a)>);
std::array b{4.1, 3.2, 4.3, 0.1, 0.2};
static_assert(std::ranges::contiguous_range<decltype(b)>);
{
// operator+(x, n) and operator+=
std::ranges::zip_view v(a, b);
auto it1 = v.begin();
auto it2 = it1 + 3;
auto [x2, y2] = *it2;
assert(&x2 == &(a[3]));
assert(&y2 == &(b[3]));
auto it3 = 3 + it1;
auto [x3, y3] = *it3;
assert(&x3 == &(a[3]));
assert(&y3 == &(b[3]));
it1 += 3;
assert(it1 == it2);
auto [x1, y1] = *it2;
assert(&x1 == &(a[3]));
assert(&y1 == &(b[3]));
using Iter = decltype(it1);
static_assert(canPlusEqual<Iter, intptr_t>);
}
{
// operator-(x, n) and operator-=
std::ranges::zip_view v(a, b);
auto it1 = v.end();
auto it2 = it1 - 3;
auto [x2, y2] = *it2;
assert(&x2 == &(a[2]));
assert(&y2 == &(b[2]));
it1 -= 3;
assert(it1 == it2);
auto [x1, y1] = *it2;
assert(&x1 == &(a[2]));
assert(&y1 == &(b[2]));
using Iter = decltype(it1);
static_assert(canMinusEqual<Iter, intptr_t>);
}
{
// operator-(x, y)
std::ranges::zip_view v(a, b);
assert((v.end() - v.begin()) == 5);
auto it1 = v.begin() + 2;
auto it2 = v.end() - 1;
assert((it1 - it2) == -2);
}
{
// in this case sentinel is computed by getting each of the underlying sentinels, so the distance
// between begin and end for each of the underlying iterators can be different
std::ranges::zip_view v{ForwardSizedView(buffer1), ForwardSizedView(buffer2)};
using View = decltype(v);
static_assert(std::ranges::common_range<View>);
static_assert(!std::ranges::random_access_range<View>);
auto it1 = v.begin();
auto it2 = v.end();
// it1 : <buffer1 + 0, buffer2 + 0>
// it2 : <buffer1 + 5, buffer2 + 9>
assert((it1 - it2) == -5);
assert((it2 - it1) == 5);
}
{
// One of the ranges is not random access
std::ranges::zip_view v(a, b, ForwardSizedView{buffer1});
using Iter = decltype(v.begin());
static_assert(!std::invocable<std::plus<>, Iter, intptr_t>);
static_assert(!std::invocable<std::plus<>, intptr_t, Iter>);
static_assert(!canPlusEqual<Iter, intptr_t>);
static_assert(!std::invocable<std::minus<>, Iter, intptr_t>);
static_assert(std::invocable<std::minus<>, Iter, Iter>);
static_assert(!canMinusEqual<Iter, intptr_t>);
}
{
// One of the ranges does not have sized sentinel
std::ranges::zip_view v(a, b, InputCommonView{buffer1});
using Iter = decltype(v.begin());
static_assert(!std::invocable<std::minus<>, Iter, Iter>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,250 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// friend constexpr bool operator==(const iterator& x, const iterator& y)
// requires (equality_­comparable<iterator_t<maybe-const<Const, Views>>> && ...);
// friend constexpr bool operator<(const iterator& x, const iterator& y)
// requires all-random-access<Const, Views...>;
// friend constexpr bool operator>(const iterator& x, const iterator& y)
// requires all-random-access<Const, Views...>;
// friend constexpr bool operator<=(const iterator& x, const iterator& y)
// requires all-random-access<Const, Views...>;
// friend constexpr bool operator>=(const iterator& x, const iterator& y)
// requires all-random-access<Const, Views...>;
// friend constexpr auto operator<=>(const iterator& x, const iterator& y)
// requires all-random-access<Const, Views...> &&
// (three_­way_­comparable<iterator_t<maybe-const<Const, Views>>> && ...);
#include <ranges>
#include <compare>
#include "test_iterators.h"
#include "../types.h"
// This is for testing that zip iterator never calls underlying iterator's >, >=, <=, !=.
// The spec indicates that zip iterator's >= is negating zip iterator's < instead of calling underlying iterator's >=.
// Declare all the operations >, >=, <= etc to make it satisfy random_access_iterator concept,
// but not define them. If the zip iterator's >,>=, <=, etc isn't implemented in the way defined by the standard
// but instead calling underlying iterator's >,>=,<=, we will get a linker error for the runtime tests and
// non-constant expression for the compile time tests.
struct LessThanIterator {
int* it_ = nullptr;
LessThanIterator() = default;
constexpr LessThanIterator(int* it) : it_(it) {}
using iterator_category = std::random_access_iterator_tag;
using value_type = int;
using difference_type = intptr_t;
constexpr int& operator*() const { return *it_; }
constexpr int& operator[](difference_type n) const { return it_[n]; }
constexpr LessThanIterator& operator++() {
++it_;
return *this;
}
constexpr LessThanIterator& operator--() {
--it_;
return *this;
}
constexpr LessThanIterator operator++(int) { return LessThanIterator(it_++); }
constexpr LessThanIterator operator--(int) { return LessThanIterator(it_--); }
constexpr LessThanIterator& operator+=(difference_type n) {
it_ += n;
return *this;
}
constexpr LessThanIterator& operator-=(difference_type n) {
it_ -= n;
return *this;
}
constexpr friend LessThanIterator operator+(LessThanIterator x, difference_type n) {
x += n;
return x;
}
constexpr friend LessThanIterator operator+(difference_type n, LessThanIterator x) {
x += n;
return x;
}
constexpr friend LessThanIterator operator-(LessThanIterator x, difference_type n) {
x -= n;
return x;
}
constexpr friend difference_type operator-(LessThanIterator x, LessThanIterator y) { return x.it_ - y.it_; }
constexpr friend bool operator==(LessThanIterator const&, LessThanIterator const&) = default;
friend bool operator!=(LessThanIterator const&, LessThanIterator const&);
constexpr friend bool operator<(LessThanIterator const& x, LessThanIterator const& y) { return x.it_ < y.it_; }
friend bool operator<=(LessThanIterator const&, LessThanIterator const&);
friend bool operator>(LessThanIterator const&, LessThanIterator const&);
friend bool operator>=(LessThanIterator const&, LessThanIterator const&);
};
static_assert(std::random_access_iterator<LessThanIterator>);
struct SmallerThanRange : IntBufferView {
using IntBufferView::IntBufferView;
constexpr LessThanIterator begin() const { return {buffer_}; }
constexpr LessThanIterator end() const { return {buffer_ + size_}; }
};
static_assert(std::ranges::random_access_range<SmallerThanRange>);
struct ForwardCommonView : IntBufferView {
using IntBufferView::IntBufferView;
using iterator = forward_iterator<int*>;
constexpr iterator begin() const { return iterator(buffer_); }
constexpr iterator end() const { return iterator(buffer_ + size_); }
};
constexpr void compareOperatorTest(auto&& iter1, auto&& iter2) {
assert(!(iter1 < iter1));
assert(iter1 < iter2);
assert(!(iter2 < iter1));
assert(iter1 <= iter1);
assert(iter1 <= iter2);
assert(!(iter2 <= iter1));
assert(!(iter1 > iter1));
assert(!(iter1 > iter2));
assert(iter2 > iter1);
assert(iter1 >= iter1);
assert(!(iter1 >= iter2));
assert(iter2 >= iter1);
assert(iter1 == iter1);
assert(!(iter1 == iter2));
assert(iter2 == iter2);
assert(!(iter1 != iter1));
assert(iter1 != iter2);
assert(!(iter2 != iter2));
}
constexpr void inequalityOperatorsDoNotExistTest(auto&& iter1, auto&& iter2) {
using Iter1 = decltype(iter1);
using Iter2 = decltype(iter2);
static_assert(!std::is_invocable_v<std::less<>, Iter1, Iter2>);
static_assert(!std::is_invocable_v<std::less_equal<>, Iter1, Iter2>);
static_assert(!std::is_invocable_v<std::greater<>, Iter1, Iter2>);
static_assert(!std::is_invocable_v<std::greater_equal<>, Iter1, Iter2>);
}
constexpr bool test() {
{
// Test a new-school iterator with operator<=>; the iterator should also have operator<=>.
using It = three_way_contiguous_iterator<int*>;
using SubRange = std::ranges::subrange<It>;
static_assert(std::three_way_comparable<It>);
using R = std::ranges::zip_view<SubRange, SubRange>;
static_assert(std::three_way_comparable<std::ranges::iterator_t<R>>);
int a[] = {1, 2, 3, 4};
int b[] = {5, 6, 7, 8, 9};
auto r = std::views::zip(SubRange(It(a), It(a + 4)), SubRange(It(b), It(b + 5)));
auto iter1 = r.begin();
auto iter2 = iter1 + 1;
compareOperatorTest(iter1, iter2);
assert((iter1 <=> iter2) == std::strong_ordering::less);
assert((iter1 <=> iter1) == std::strong_ordering::equal);
assert((iter2 <=> iter1) == std::strong_ordering::greater);
}
{
// Test an old-school iterator with no operator<=>; the transform iterator shouldn't have
// operator<=> either.
using It = random_access_iterator<int*>;
using Subrange = std::ranges::subrange<It>;
static_assert(!std::three_way_comparable<It>);
using R = std::ranges::zip_view<Subrange, Subrange>;
static_assert(!std::three_way_comparable<std::ranges::iterator_t<R>>);
int a[] = {1, 2, 3, 4};
int b[] = {5, 6, 7, 8, 9};
auto r = std::views::zip(Subrange(It(a), It(a + 4)), Subrange(It(b), It(b + 5)));
auto iter1 = r.begin();
auto iter2 = iter1 + 1;
compareOperatorTest(iter1, iter2);
}
{
// non random_access_range
int buffer1[1] = {1};
int buffer2[2] = {1, 2};
std::ranges::zip_view v{InputCommonView(buffer1), InputCommonView(buffer2)};
using View = decltype(v);
static_assert(!std::ranges::forward_range<View>);
static_assert(std::ranges::input_range<View>);
static_assert(std::ranges::common_range<View>);
auto it1 = v.begin();
auto it2 = v.end();
assert(it1 != it2);
++it1;
assert(it1 == it2);
inequalityOperatorsDoNotExistTest(it1, it2);
}
{
// in this case sentinel is computed by getting each of the underlying sentinel, so only one
// underlying iterator is comparing equal
int buffer1[1] = {1};
int buffer2[2] = {1, 2};
std::ranges::zip_view v{ForwardCommonView(buffer1), ForwardCommonView(buffer2)};
using View = decltype(v);
static_assert(std::ranges::common_range<View>);
static_assert(!std::ranges::bidirectional_range<View>);
auto it1 = v.begin();
auto it2 = v.end();
assert(it1 != it2);
++it1;
// it1: <buffer1 + 1, buffer2 + 1>
// it2: <buffer1 + 1, buffer2 + 2>
assert(it1 == it2);
inequalityOperatorsDoNotExistTest(it1, it2);
}
{
// only < and == are needed
int a[] = {1, 2, 3, 4};
int b[] = {5, 6, 7, 8, 9};
auto r = std::views::zip(SmallerThanRange(a), SmallerThanRange(b));
auto iter1 = r.begin();
auto iter2 = iter1 + 1;
compareOperatorTest(iter1, iter2);
}
{
// underlying iterator does not support ==
using IterNoEqualView = BasicView<cpp20_input_iterator<int*>, sentinel_wrapper<cpp20_input_iterator<int*>>>;
int buffer[] = {1};
std::ranges::zip_view r(IterNoEqualView{buffer});
auto it = r.begin();
using Iter = decltype(it);
static_assert(!std::invocable<std::equal_to<>, Iter, Iter>);
inequalityOperatorsDoNotExistTest(it, it);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,74 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// iterator() = default;
#include <ranges>
#include <tuple>
#include "../types.h"
struct PODIter {
int i; // deliberately uninitialised
using iterator_category = std::random_access_iterator_tag;
using value_type = int;
using difference_type = intptr_t;
constexpr int operator*() const { return i; }
constexpr PODIter& operator++() { return *this; }
constexpr void operator++(int) {}
friend constexpr bool operator==(const PODIter&, const PODIter&) = default;
};
struct IterDefaultCtrView : std::ranges::view_base {
PODIter begin() const;
PODIter end() const;
};
struct IterNoDefaultCtrView : std::ranges::view_base {
cpp20_input_iterator<int*> begin() const;
sentinel_wrapper<cpp20_input_iterator<int*>> end() const;
};
template <class... Views>
using zip_iter = std::ranges::iterator_t<std::ranges::zip_view<Views...>>;
static_assert(!std::default_initializable<zip_iter<IterNoDefaultCtrView>>);
static_assert(!std::default_initializable<zip_iter<IterNoDefaultCtrView, IterDefaultCtrView>>);
static_assert(!std::default_initializable<zip_iter<IterNoDefaultCtrView, IterNoDefaultCtrView>>);
static_assert(std::default_initializable<zip_iter<IterDefaultCtrView>>);
static_assert(std::default_initializable<zip_iter<IterDefaultCtrView, IterDefaultCtrView>>);
constexpr bool test() {
using ZipIter = zip_iter<IterDefaultCtrView>;
{
ZipIter iter;
auto [x] = *iter;
assert(x == 0); // PODIter has to be initialised to have value 0
}
{
ZipIter iter = {};
auto [x] = *iter;
assert(x == 0); // PODIter has to be initialised to have value 0
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,63 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr iterator(iterator<!Const> i)
// requires Const && (convertible_­to<iterator_t<Views>,
// iterator_t<maybe-const<Const, Views>>> && ...);
#include <ranges>
#include <cassert>
#include <tuple>
#include "../types.h"
using ConstIterIncompatibleView = BasicView<forward_iterator<int*>, forward_iterator<int*>,
random_access_iterator<const int*>, random_access_iterator<const int*>>;
static_assert(!std::convertible_to<std::ranges::iterator_t<ConstIterIncompatibleView>,
std::ranges::iterator_t<const ConstIterIncompatibleView>>);
constexpr bool test() {
int buffer[3] = {1, 2, 3};
{
std::ranges::zip_view v(NonSimpleCommon{buffer});
auto iter1 = v.begin();
std::ranges::iterator_t<const decltype(v)> iter2 = iter1;
assert(iter1 == iter2);
static_assert(!std::is_same_v<decltype(iter1), decltype(iter2)>);
// We cannot create a non-const iterator from a const iterator.
static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
}
{
// underlying non-const to const not convertible
std::ranges::zip_view v(ConstIterIncompatibleView{buffer});
auto iter1 = v.begin();
auto iter2 = std::as_const(v).begin();
static_assert(!std::is_same_v<decltype(iter1), decltype(iter2)>);
static_assert(!std::constructible_from<decltype(iter1), decltype(iter2)>);
static_assert(!std::constructible_from<decltype(iter2), decltype(iter1)>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,95 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr iterator& operator--() requires all-bidirectional<Const, Views...>;
// constexpr iterator operator--(int) requires all-bidirectional<Const, Views...>;
#include <array>
#include <cassert>
#include <ranges>
#include <tuple>
#include "../types.h"
template <class Iter>
concept canDecrement = requires(Iter it) { --it; } || requires(Iter it) { it--; };
struct NonBidi : IntBufferView {
using IntBufferView::IntBufferView;
using iterator = forward_iterator<int*>;
constexpr iterator begin() const { return iterator(buffer_); }
constexpr sentinel_wrapper<iterator> end() const { return sentinel_wrapper<iterator>(iterator(buffer_ + size_)); }
};
constexpr bool test() {
std::array a{1, 2, 3, 4};
std::array b{4.1, 3.2, 4.3};
{
// all random access
std::ranges::zip_view v(a, b, std::views::iota(0, 5));
auto it = v.end();
using Iter = decltype(it);
static_assert(std::is_same_v<decltype(--it), Iter&>);
auto& it_ref = --it;
assert(&it_ref == &it);
assert(&(std::get<0>(*it)) == &(a[2]));
assert(&(std::get<1>(*it)) == &(b[2]));
assert(std::get<2>(*it) == 2);
static_assert(std::is_same_v<decltype(it--), Iter>);
it--;
assert(&(std::get<0>(*it)) == &(a[1]));
assert(&(std::get<1>(*it)) == &(b[1]));
assert(std::get<2>(*it) == 1);
}
{
// all bidi+
int buffer[2] = {1, 2};
std::ranges::zip_view v(BidiCommonView{buffer}, std::views::iota(0, 5));
auto it = v.begin();
using Iter = decltype(it);
++it;
++it;
static_assert(std::is_same_v<decltype(--it), Iter&>);
auto& it_ref = --it;
assert(&it_ref == &it);
assert(it == ++v.begin());
static_assert(std::is_same_v<decltype(it--), Iter>);
auto tmp = it--;
assert(it == v.begin());
assert(tmp == ++v.begin());
}
{
// non bidi
int buffer[3] = {4, 5, 6};
std::ranges::zip_view v(a, NonBidi{buffer});
using Iter = std::ranges::iterator_t<decltype(v)>;
static_assert(!canDecrement<Iter>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,80 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr auto operator*() const;
#include <array>
#include <cassert>
#include <ranges>
#include <tuple>
#include "../types.h"
constexpr bool test() {
std::array a{1, 2, 3, 4};
std::array b{4.1, 3.2, 4.3};
{
// single range
std::ranges::zip_view v(a);
auto it = v.begin();
assert(&(std::get<0>(*it)) == &(a[0]));
static_assert(std::is_same_v<decltype(*it), std::tuple<int&>>);
}
{
// operator* is const
std::ranges::zip_view v(a);
const auto it = v.begin();
assert(&(std::get<0>(*it)) == &(a[0]));
}
{
// two ranges with different types
std::ranges::zip_view v(a, b);
auto it = v.begin();
auto [x, y] = *it;
assert(&x == &(a[0]));
assert(&y == &(b[0]));
static_assert(std::is_same_v<decltype(*it), std::pair<int&, double&>>);
x = 5;
y = 0.1;
assert(a[0] == 5);
assert(b[0] == 0.1);
}
{
// underlying range with prvalue range_reference_t
std::ranges::zip_view v(a, b, std::views::iota(0, 5));
auto it = v.begin();
assert(&(std::get<0>(*it)) == &(a[0]));
assert(&(std::get<1>(*it)) == &(b[0]));
assert(std::get<2>(*it) == 0);
static_assert(std::is_same_v<decltype(*it), std::tuple<int&, double&, int>>);
}
{
// const-correctness
std::ranges::zip_view v(a, std::as_const(a));
auto it = v.begin();
assert(&(std::get<0>(*it)) == &(a[0]));
assert(&(std::get<1>(*it)) == &(a[0]));
static_assert(std::is_same_v<decltype(*it), std::pair<int&, int const&>>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,135 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr iterator& operator++();
// constexpr void operator++(int);
// constexpr iterator operator++(int) requires all_forward<Const, Views...>;
#include <array>
#include <cassert>
#include <ranges>
#include <tuple>
#include "../types.h"
struct InputRange : IntBufferView {
using IntBufferView::IntBufferView;
using iterator = cpp20_input_iterator<int*>;
constexpr iterator begin() const { return iterator(buffer_); }
constexpr sentinel_wrapper<iterator> end() const { return sentinel_wrapper<iterator>(iterator(buffer_ + size_)); }
};
constexpr bool test() {
std::array a{1, 2, 3, 4};
std::array b{4.1, 3.2, 4.3};
{
// random/contiguous
std::ranges::zip_view v(a, b, std::views::iota(0, 5));
auto it = v.begin();
using Iter = decltype(it);
assert(&(std::get<0>(*it)) == &(a[0]));
assert(&(std::get<1>(*it)) == &(b[0]));
assert(std::get<2>(*it) == 0);
static_assert(std::is_same_v<decltype(++it), Iter&>);
auto& it_ref = ++it;
assert(&it_ref == &it);
assert(&(std::get<0>(*it)) == &(a[1]));
assert(&(std::get<1>(*it)) == &(b[1]));
assert(std::get<2>(*it) == 1);
static_assert(std::is_same_v<decltype(it++), Iter>);
auto original = it;
auto copy = it++;
assert(original == copy);
assert(&(std::get<0>(*it)) == &(a[2]));
assert(&(std::get<1>(*it)) == &(b[2]));
assert(std::get<2>(*it) == 2);
}
{
// bidi
int buffer[2] = {1, 2};
std::ranges::zip_view v(BidiCommonView{buffer});
auto it = v.begin();
using Iter = decltype(it);
assert(&(std::get<0>(*it)) == &(buffer[0]));
static_assert(std::is_same_v<decltype(++it), Iter&>);
auto& it_ref = ++it;
assert(&it_ref == &it);
assert(&(std::get<0>(*it)) == &(buffer[1]));
static_assert(std::is_same_v<decltype(it++), Iter>);
auto original = it;
auto copy = it++;
assert(copy == original);
assert(&(std::get<0>(*it)) == &(buffer[2]));
}
{
// forward
int buffer[2] = {1, 2};
std::ranges::zip_view v(ForwardSizedView{buffer});
auto it = v.begin();
using Iter = decltype(it);
assert(&(std::get<0>(*it)) == &(buffer[0]));
static_assert(std::is_same_v<decltype(++it), Iter&>);
auto& it_ref = ++it;
assert(&it_ref == &it);
assert(&(std::get<0>(*it)) == &(buffer[1]));
static_assert(std::is_same_v<decltype(it++), Iter>);
auto original = it;
auto copy = it++;
assert(copy == original);
assert(&(std::get<0>(*it)) == &(buffer[2]));
}
{
// all input+
int buffer[3] = {4, 5, 6};
std::ranges::zip_view v(a, InputRange{buffer});
auto it = v.begin();
using Iter = decltype(it);
assert(&(std::get<0>(*it)) == &(a[0]));
assert(&(std::get<1>(*it)) == &(buffer[0]));
static_assert(std::is_same_v<decltype(++it), Iter&>);
auto& it_ref = ++it;
assert(&it_ref == &it);
assert(&(std::get<0>(*it)) == &(a[1]));
assert(&(std::get<1>(*it)) == &(buffer[1]));
static_assert(std::is_same_v<decltype(it++), void>);
it++;
assert(&(std::get<0>(*it)) == &(a[2]));
assert(&(std::get<1>(*it)) == &(buffer[2]));
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,77 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// friend constexpr auto iter_move(const iterator& i) noexcept(see below);
#include <array>
#include <cassert>
#include <iterator>
#include <ranges>
#include <tuple>
#include "../types.h"
struct ThrowingMove {
ThrowingMove() = default;
ThrowingMove(ThrowingMove&&){};
};
constexpr bool test() {
{
// underlying iter_move noexcept
std::array a1{1, 2, 3, 4};
const std::array a2{3.0, 4.0};
std::ranges::zip_view v(a1, a2, std::views::iota(3L));
assert(std::ranges::iter_move(v.begin()) == std::make_tuple(1, 3.0, 3L));
static_assert(std::is_same_v<decltype(std::ranges::iter_move(v.begin())), std::tuple<int&&, const double&&, long>>);
auto it = v.begin();
static_assert(noexcept(std::ranges::iter_move(it)));
}
{
// underlying iter_move may throw
auto throwingMoveRange =
std::views::iota(0, 2) | std::views::transform([](auto) noexcept { return ThrowingMove{}; });
std::ranges::zip_view v(throwingMoveRange);
auto it = v.begin();
static_assert(!noexcept(std::ranges::iter_move(it)));
}
{
// underlying iterators' iter_move are called through ranges::iter_move
adltest::IterMoveSwapRange r1{}, r2{};
assert(r1.iter_move_called_times == 0);
assert(r2.iter_move_called_times == 0);
std::ranges::zip_view v(r1, r2);
auto it = v.begin();
{
[[maybe_unused]] auto&& i = std::ranges::iter_move(it);
assert(r1.iter_move_called_times == 1);
assert(r2.iter_move_called_times == 1);
}
{
[[maybe_unused]] auto&& i = std::ranges::iter_move(it);
assert(r1.iter_move_called_times == 2);
assert(r2.iter_move_called_times == 2);
}
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,88 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// friend constexpr void iter_swap(const iterator& l, const iterator& r) noexcept(see below)
// requires (indirectly_­swappable<iterator_t<maybe-const<Const, Views>>> && ...);
#include <array>
#include <cassert>
#include <ranges>
#include "../types.h"
struct ThrowingMove {
ThrowingMove() = default;
ThrowingMove(ThrowingMove&&){};
ThrowingMove& operator=(ThrowingMove&&){return *this;}
};
constexpr bool test() {
{
std::array a1{1, 2, 3, 4};
std::array a2{0.1, 0.2, 0.3};
std::ranges::zip_view v(a1, a2);
auto iter1 = v.begin();
auto iter2 = ++v.begin();
std::ranges::iter_swap(iter1, iter2);
assert(a1[0] == 2);
assert(a1[1] == 1);
assert(a2[0] == 0.2);
assert(a2[1] == 0.1);
auto [x1, y1] = *iter1;
assert(&x1 == &a1[0]);
assert(&y1 == &a2[0]);
auto [x2, y2] = *iter2;
assert(&x2 == &a1[1]);
assert(&y2 == &a2[1]);
static_assert(noexcept(std::ranges::iter_swap(iter1, iter2)));
}
{
// underlying iter_swap may throw
std::array<ThrowingMove, 2> iterSwapMayThrow{};
std::ranges::zip_view v(iterSwapMayThrow);
auto iter1 = v.begin();
auto iter2 = ++v.begin();
static_assert(!noexcept(std::ranges::iter_swap(iter1, iter2)));
}
{
// underlying iterators' iter_move are called through ranges::iter_swap
adltest::IterMoveSwapRange r1, r2;
assert(r1.iter_swap_called_times == 0);
assert(r2.iter_swap_called_times == 0);
std::ranges::zip_view v{r1, r2};
auto it1 = v.begin();
auto it2 = std::ranges::next(it1, 3);
std::ranges::iter_swap(it1, it2);
assert(r1.iter_swap_called_times == 2);
assert(r2.iter_swap_called_times == 2);
std::ranges::iter_swap(it1, it2);
assert(r1.iter_swap_called_times == 4);
assert(r2.iter_swap_called_times == 4);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,185 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// Iterator traits and member typedefs in zip_view::<iterator>.
#include <array>
#include <ranges>
#include <tuple>
#include "test_iterators.h"
#include "../types.h"
template <class T>
struct ForwardView : std::ranges::view_base {
forward_iterator<T*> begin() const;
sentinel_wrapper<forward_iterator<T*>> end() const;
};
template <class T>
struct InputView : std::ranges::view_base {
cpp17_input_iterator<T*> begin() const;
sentinel_wrapper<cpp17_input_iterator<T*>> end() const;
};
template <class T>
concept HasIterCategory = requires { typename T::iterator_category; };
template <class T>
struct DiffTypeIter {
using iterator_category = std::input_iterator_tag;
using value_type = int;
using difference_type = T;
int operator*() const;
DiffTypeIter& operator++();
void operator++(int);
friend constexpr bool operator==(DiffTypeIter, DiffTypeIter) = default;
};
template <class T>
struct DiffTypeRange {
DiffTypeIter<T> begin() const;
DiffTypeIter<T> end() const;
};
struct Foo {};
struct Bar {};
struct ConstVeryDifferentRange {
int* begin();
int* end();
forward_iterator<double*> begin() const;
forward_iterator<double*> end() const;
};
void test() {
int buffer[] = {1, 2, 3, 4};
{
// 2 views should have pair value_type
// random_access_iterator_tag
std::ranges::zip_view v(buffer, buffer);
using Iter = decltype(v.begin());
static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>);
static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
static_assert(std::is_same_v<Iter::value_type, std::pair<int, int>>);
static_assert(HasIterCategory<Iter>);
}
{
// !=2 views should have tuple value_type
std::ranges::zip_view v(buffer, buffer, buffer);
using Iter = decltype(v.begin());
static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>);
static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
static_assert(std::is_same_v<Iter::value_type, std::tuple<int, int, int>>);
static_assert(HasIterCategory<Iter>);
}
{
// bidirectional_iterator_tag
std::ranges::zip_view v(BidiCommonView{buffer});
using Iter = decltype(v.begin());
static_assert(std::is_same_v<Iter::iterator_concept, std::bidirectional_iterator_tag>);
static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>);
}
{
// forward_iterator_tag
using Iter = std::ranges::iterator_t<std::ranges::zip_view<ForwardView<int>>>;
static_assert(std::is_same_v<Iter::iterator_concept, std::forward_iterator_tag>);
static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>);
static_assert(HasIterCategory<Iter>);
}
{
// nested zip_view
std::ranges::zip_view v(buffer, buffer);
std::ranges::zip_view v2(buffer, v);
using Iter = decltype(v2.begin());
static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>);
static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
static_assert(std::is_same_v<Iter::value_type, std::pair<int, std::pair<int, int>>>);
static_assert(HasIterCategory<Iter>);
}
{
// input_iterator_tag
using Iter = std::ranges::iterator_t<std::ranges::zip_view<InputView<int>>>;
static_assert(std::is_same_v<Iter::iterator_concept, std::input_iterator_tag>);
static_assert(!HasIterCategory<Iter>);
static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>);
}
{
// difference_type of single view
std::ranges::zip_view v{DiffTypeRange<intptr_t>{}};
using Iter = decltype(v.begin());
static_assert(std::is_same_v<Iter::difference_type, intptr_t>);
}
{
// difference_type of multiple views should be the common type
std::ranges::zip_view v{DiffTypeRange<intptr_t>{}, DiffTypeRange<std::ptrdiff_t>{}};
using Iter = decltype(v.begin());
static_assert(std::is_same_v<Iter::difference_type, std::common_type_t<intptr_t, std::ptrdiff_t>>);
}
const std::array foos{Foo{}};
std::array bars{Bar{}, Bar{}};
{
// value_type of single view
std::ranges::zip_view v{foos};
using Iter = decltype(v.begin());
static_assert(std::is_same_v<Iter::value_type, std::tuple<Foo>>);
}
{
// value_type of multiple views with different value_type
std::ranges::zip_view v{foos, bars};
using Iter = decltype(v.begin());
static_assert(std::is_same_v<Iter::value_type, std::pair<Foo, Bar>>);
}
{
// const-iterator different from iterator
std::ranges::zip_view v{ConstVeryDifferentRange{}};
using Iter = decltype(v.begin());
using ConstIter = decltype(std::as_const(v).begin());
static_assert(std::is_same_v<Iter::iterator_concept, std::random_access_iterator_tag>);
static_assert(std::is_same_v<Iter::iterator_category, std::input_iterator_tag>);
static_assert(std::is_same_v<Iter::difference_type, std::ptrdiff_t>);
static_assert(std::is_same_v<Iter::value_type, std::tuple<int>>);
static_assert(std::is_same_v<ConstIter::iterator_concept, std::forward_iterator_tag>);
static_assert(std::is_same_v<ConstIter::iterator_category, std::input_iterator_tag>);
static_assert(std::is_same_v<ConstIter::difference_type, std::ptrdiff_t>);
static_assert(std::is_same_v<ConstIter::value_type, std::tuple<double>>);
}
}

View File

@ -0,0 +1,83 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges, no-exceptions
// If the invocation of any non-const member function of `iterator` exits via an
// exception, the iterator acquires a singular value.
#include <ranges>
#include <tuple>
#include "../types.h"
struct ThrowOnIncrementIterator {
int* it_;
using value_type = int;
using difference_type = std::intptr_t;
using iterator_concept = std::input_iterator_tag;
ThrowOnIncrementIterator() = default;
explicit ThrowOnIncrementIterator(int* it) : it_(it) {}
ThrowOnIncrementIterator& operator++() {
++it_;
throw 5;
return *this;
}
void operator++(int) { ++it_; }
int& operator*() const { return *it_; }
friend bool operator==(ThrowOnIncrementIterator const&, ThrowOnIncrementIterator const&) = default;
};
struct ThrowOnIncrementView : IntBufferView {
ThrowOnIncrementIterator begin() const { return ThrowOnIncrementIterator{buffer_}; }
ThrowOnIncrementIterator end() const { return ThrowOnIncrementIterator{buffer_ + size_}; }
};
// Cannot run the test at compile time because it is not allowed to throw exceptions
void test() {
int buffer[] = {1, 2, 3};
{
// zip iterator should be able to be destroyed after member function throws
std::ranges::zip_view v{ThrowOnIncrementView{buffer}};
auto it = v.begin();
try {
++it;
assert(false); // should not be reached as the above expression should throw.
} catch (int e) {
assert(e == 5);
}
}
{
// zip iterator should be able to be assigned after member function throws
std::ranges::zip_view v{ThrowOnIncrementView{buffer}};
auto it = v.begin();
try {
++it;
assert(false); // should not be reached as the above expression should throw.
} catch (int e) {
assert(e == 5);
}
it = v.begin();
auto [x] = *it;
assert(x == 1);
}
}
int main(int, char**) {
test();
return 0;
}

View File

@ -0,0 +1,61 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr auto operator[](difference_type n) const requires
// all_random_access<Const, Views...>
#include <ranges>
#include <cassert>
#include "../types.h"
constexpr bool test() {
int buffer[8] = {1, 2, 3, 4, 5, 6, 7, 8};
{
// random_access_range
std::ranges::zip_view v(SizedRandomAccessView{buffer}, std::views::iota(0));
auto it = v.begin();
assert(it[0] == *it);
assert(it[2] == *(it + 2));
assert(it[4] == *(it + 4));
static_assert(std::is_same_v<decltype(it[2]), std::pair<int&, int>>);
}
{
// contiguous_range
std::ranges::zip_view v(ContiguousCommonView{buffer}, ContiguousCommonView{buffer});
auto it = v.begin();
assert(it[0] == *it);
assert(it[2] == *(it + 2));
assert(it[4] == *(it + 4));
static_assert(std::is_same_v<decltype(it[2]), std::pair<int&, int&>>);
}
{
// non random_access_range
std::ranges::zip_view v(BidiCommonView{buffer});
auto iter = v.begin();
const auto canSubscript = [](auto&& it) { return requires { it[0]; }; };
static_assert(!canSubscript(iter));
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,332 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// test if zip_view models input_range, forward_range, bidirectional_range,
// random_access_range, contiguous_range, common_range
// sized_range
#include <cassert>
#include <concepts>
#include <ranges>
#include <tuple>
#include <utility>
#include "types.h"
void testConceptPair() {
int buffer1[2] = {1, 2};
int buffer2[3] = {1, 2, 3};
{
std::ranges::zip_view v{ContiguousCommonView{buffer1}, ContiguousCommonView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{ContiguousNonCommonView{buffer1}, ContiguousNonCommonView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{ContiguousNonCommonSized{buffer1}, ContiguousNonCommonSized{buffer2}};
using View = decltype(v);
static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{SizedRandomAccessView{buffer1}, ContiguousCommonView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{SizedRandomAccessView{buffer1}, SizedRandomAccessView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{NonSizedRandomAccessView{buffer1}, NonSizedRandomAccessView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{BidiCommonView{buffer1}, SizedRandomAccessView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::bidirectional_range<View>);
static_assert(!std::ranges::random_access_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{BidiCommonView{buffer1}, BidiCommonView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::bidirectional_range<View>);
static_assert(!std::ranges::random_access_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{BidiCommonView{buffer1}, ForwardSizedView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::forward_range<View>);
static_assert(!std::ranges::bidirectional_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{BidiNonCommonView{buffer1}, ForwardSizedView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::forward_range<View>);
static_assert(!std::ranges::bidirectional_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{ForwardSizedView{buffer1}, ForwardSizedView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::forward_range<View>);
static_assert(!std::ranges::bidirectional_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{ForwardSizedNonCommon{buffer1}, ForwardSizedView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::forward_range<View>);
static_assert(!std::ranges::bidirectional_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{InputCommonView{buffer1}, ForwardSizedView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::input_range<View>);
static_assert(!std::ranges::forward_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{InputCommonView{buffer1}, InputCommonView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::input_range<View>);
static_assert(!std::ranges::forward_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{InputNonCommonView{buffer1}, InputCommonView{buffer2}};
using View = decltype(v);
static_assert(std::ranges::input_range<View>);
static_assert(!std::ranges::forward_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
}
void testConceptTuple() {
int buffer1[2] = {1, 2};
int buffer2[3] = {1, 2, 3};
int buffer3[4] = {1, 2, 3, 4};
// TODO: uncomment all the static_asserts once [tuple.tuple] section in P2321R2 is implemented
// This is because convertible_to<tuple<int&,int&,int&>&, tuple<int,int,int>> is false without
// the above implementation, thus the zip iterator does not model indirectly_readable
{
std::ranges::zip_view v{ContiguousCommonView{buffer1}, ContiguousCommonView{buffer2},
ContiguousCommonView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{ContiguousNonCommonView{buffer1}, ContiguousNonCommonView{buffer2},
ContiguousNonCommonView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{ContiguousNonCommonSized{buffer1}, ContiguousNonCommonSized{buffer2},
ContiguousNonCommonSized{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{SizedRandomAccessView{buffer1}, ContiguousCommonView{buffer2},
ContiguousCommonView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{SizedRandomAccessView{buffer1}, SizedRandomAccessView{buffer2},
SizedRandomAccessView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{NonSizedRandomAccessView{buffer1}, NonSizedRandomAccessView{buffer2},
NonSizedRandomAccessView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::random_access_range<View>);
static_assert(!std::ranges::contiguous_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{BidiCommonView{buffer1}, SizedRandomAccessView{buffer2}, SizedRandomAccessView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::bidirectional_range<View>);
static_assert(!std::ranges::random_access_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{BidiCommonView{buffer1}, BidiCommonView{buffer2}, BidiCommonView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::bidirectional_range<View>);
static_assert(!std::ranges::random_access_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{BidiCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::forward_range<View>);
static_assert(!std::ranges::bidirectional_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{BidiNonCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::forward_range<View>);
static_assert(!std::ranges::bidirectional_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{ForwardSizedView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::forward_range<View>);
static_assert(!std::ranges::bidirectional_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{ForwardSizedNonCommon{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::forward_range<View>);
static_assert(!std::ranges::bidirectional_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{InputCommonView{buffer1}, ForwardSizedView{buffer2}, ForwardSizedView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::input_range<View>);
static_assert(!std::ranges::forward_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{InputCommonView{buffer1}, InputCommonView{buffer2}, InputCommonView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::input_range<View>);
static_assert(!std::ranges::forward_range<View>);
static_assert(std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
{
std::ranges::zip_view v{InputNonCommonView{buffer1}, InputCommonView{buffer2}, InputCommonView{buffer3}};
using View = decltype(v);
// static_assert(std::ranges::input_range<View>);
static_assert(!std::ranges::forward_range<View>);
static_assert(!std::ranges::common_range<View>);
static_assert(!std::ranges::sized_range<View>);
}
}
using OutputIter = cpp17_output_iterator<int*>;
static_assert(std::output_iterator<OutputIter, int>);
struct OutputView : std::ranges::view_base {
OutputIter begin() const;
sentinel_wrapper<OutputIter> end() const;
};
static_assert(std::ranges::output_range<OutputView, int>);
static_assert(!std::ranges::input_range<OutputView>);
template <class... Ts>
concept zippable = requires {
typename std::ranges::zip_view<Ts...>;
};
// output_range is not supported
static_assert(!zippable<OutputView>);
static_assert(!zippable<SimpleCommon, OutputView>);
static_assert(zippable<SimpleCommon>);

View File

@ -0,0 +1,51 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// sentinel() = default;
#include <cassert>
#include <ranges>
#include <tuple>
struct PODSentinel {
bool b; // deliberately uninitialised
friend constexpr bool operator==(int*, const PODSentinel& s) { return s.b; }
};
struct Range : std::ranges::view_base {
int* begin() const;
PODSentinel end();
};
constexpr bool test() {
{
using R = std::ranges::zip_view<Range>;
using Sentinel = std::ranges::sentinel_t<R>;
static_assert(!std::is_same_v<Sentinel, std::ranges::iterator_t<R>>);
std::ranges::iterator_t<R> it;
Sentinel s1;
assert(it != s1); // PODSentinel.b is initialised to false
Sentinel s2 = {};
assert(it != s2); // PODSentinel.b is initialised to false
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,76 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr sentinel(sentinel<!Const> s);
#include <cassert>
#include <ranges>
#include "../types.h"
template <class T>
struct convertible_sentinel_wrapper {
explicit convertible_sentinel_wrapper() = default;
constexpr convertible_sentinel_wrapper(const T& it) : it_(it) {}
template <class U>
requires std::convertible_to<const U&, T>
constexpr convertible_sentinel_wrapper(const convertible_sentinel_wrapper<U>& other) : it_(other.it_) {}
constexpr friend bool operator==(convertible_sentinel_wrapper const& self, const T& other) {
return self.it_ == other;
}
T it_;
};
struct NonSimpleNonCommonConvertibleView : IntBufferView {
using IntBufferView::IntBufferView;
constexpr int* begin() { return buffer_; }
constexpr const int* begin() const { return buffer_; }
constexpr convertible_sentinel_wrapper<int*> end() { return convertible_sentinel_wrapper<int*>(buffer_ + size_); }
constexpr convertible_sentinel_wrapper<const int*> end() const {
return convertible_sentinel_wrapper<const int*>(buffer_ + size_);
}
};
static_assert(!std::ranges::common_range<NonSimpleNonCommonConvertibleView>);
static_assert(std::ranges::random_access_range<NonSimpleNonCommonConvertibleView>);
static_assert(!std::ranges::sized_range<NonSimpleNonCommonConvertibleView>);
static_assert(std::convertible_to<std::ranges::sentinel_t<NonSimpleNonCommonConvertibleView>,
std::ranges::sentinel_t<NonSimpleNonCommonConvertibleView const>>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommonConvertibleView>);
constexpr bool test() {
int buffer1[4] = {1, 2, 3, 4};
int buffer2[5] = {1, 2, 3, 4, 5};
std::ranges::zip_view v{NonSimpleNonCommonConvertibleView(buffer1), NonSimpleNonCommonConvertibleView(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
auto sent1 = v.end();
std::ranges::sentinel_t<const decltype(v)> sent2 = sent1;
static_assert(!std::is_same_v<decltype(sent1), decltype(sent2)>);
assert(v.begin() != sent2);
assert(std::as_const(v).begin() != sent2);
assert(v.begin() + 4 == sent2);
assert(std::as_const(v).begin() + 4 == sent2);
// Cannot create a non-const iterator from a const iterator.
static_assert(!std::constructible_from<decltype(sent1), decltype(sent2)>);
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,156 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// template<bool OtherConst>
// requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
// friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
#include <cassert>
#include <compare>
#include <ranges>
#include <tuple>
#include "../types.h"
using Iterator = random_access_iterator<int*>;
using ConstIterator = random_access_iterator<const int*>;
template <bool Const>
struct ComparableSentinel {
using Iter = std::conditional_t<Const, ConstIterator, Iterator>;
Iter iter_;
explicit ComparableSentinel() = default;
constexpr explicit ComparableSentinel(const Iter& it) : iter_(it) {}
constexpr friend bool operator==(const Iterator& i, const ComparableSentinel& s) { return base(i) == base(s.iter_); }
constexpr friend bool operator==(const ConstIterator& i, const ComparableSentinel& s) {
return base(i) == base(s.iter_);
}
};
struct ComparableView : IntBufferView {
using IntBufferView::IntBufferView;
constexpr auto begin() { return Iterator(buffer_); }
constexpr auto begin() const { return ConstIterator(buffer_); }
constexpr auto end() { return ComparableSentinel<false>(Iterator(buffer_ + size_)); }
constexpr auto end() const { return ComparableSentinel<true>(ConstIterator(buffer_ + size_)); }
};
struct ConstIncompatibleView : std::ranges::view_base {
cpp17_input_iterator<int*> begin();
forward_iterator<const int*> begin() const;
sentinel_wrapper<cpp17_input_iterator<int*>> end();
sentinel_wrapper<forward_iterator<const int*>> end() const;
};
// clang-format off
template <class Iter, class Sent>
concept EqualComparable = std::invocable<std::equal_to<>, const Iter&, const Sent&>;
// clang-format on
constexpr bool test() {
int buffer1[4] = {1, 2, 3, 4};
int buffer2[5] = {1, 2, 3, 4, 5};
int buffer3[8] = {1, 2, 3, 4, 5, 6, 7, 8};
{
// simple-view: const and non-const have the same iterator/sentinel type
std::ranges::zip_view v{SimpleNonCommon(buffer1), SimpleNonCommon(buffer2), SimpleNonCommon(buffer3)};
static_assert(!std::ranges::common_range<decltype(v)>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<decltype(v)>);
assert(v.begin() != v.end());
assert(v.begin() + 1 != v.end());
assert(v.begin() + 2 != v.end());
assert(v.begin() + 3 != v.end());
assert(v.begin() + 4 == v.end());
}
{
// !simple-view: const and non-const have different iterator/sentinel types
std::ranges::zip_view v{NonSimpleNonCommon(buffer1), SimpleNonCommon(buffer2), SimpleNonCommon(buffer3)};
static_assert(!std::ranges::common_range<decltype(v)>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<decltype(v)>);
assert(v.begin() != v.end());
assert(v.begin() + 4 == v.end());
// const_iterator (const int*) converted to iterator (int*)
assert(v.begin() + 4 == std::as_const(v).end());
using Iter = std::ranges::iterator_t<decltype(v)>;
using ConstIter = std::ranges::iterator_t<const decltype(v)>;
static_assert(!std::is_same_v<Iter, ConstIter>);
using Sentinel = std::ranges::sentinel_t<decltype(v)>;
using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
static_assert(EqualComparable<Iter, Sentinel>);
static_assert(!EqualComparable<ConstIter, Sentinel>);
static_assert(EqualComparable<Iter, ConstSentinel>);
static_assert(EqualComparable<ConstIter, ConstSentinel>);
}
{
// underlying const/non-const sentinel can be compared with both const/non-const iterator
std::ranges::zip_view v{ComparableView(buffer1), ComparableView(buffer2)};
static_assert(!std::ranges::common_range<decltype(v)>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<decltype(v)>);
assert(v.begin() != v.end());
assert(v.begin() + 4 == v.end());
assert(std::as_const(v).begin() + 4 == v.end());
assert(std::as_const(v).begin() + 4 == std::as_const(v).end());
assert(v.begin() + 4 == std::as_const(v).end());
using Iter = std::ranges::iterator_t<decltype(v)>;
using ConstIter = std::ranges::iterator_t<const decltype(v)>;
static_assert(!std::is_same_v<Iter, ConstIter>);
using Sentinel = std::ranges::sentinel_t<decltype(v)>;
using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
static_assert(EqualComparable<Iter, Sentinel>);
static_assert(EqualComparable<ConstIter, Sentinel>);
static_assert(EqualComparable<Iter, ConstSentinel>);
static_assert(EqualComparable<ConstIter, ConstSentinel>);
}
{
// underlying const/non-const sentinel cannot be compared with non-const/const iterator
std::ranges::zip_view v{ComparableView(buffer1), ConstIncompatibleView{}};
static_assert(!std::ranges::common_range<decltype(v)>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<decltype(v)>);
using Iter = std::ranges::iterator_t<decltype(v)>;
using ConstIter = std::ranges::iterator_t<const decltype(v)>;
static_assert(!std::is_same_v<Iter, ConstIter>);
using Sentinel = std::ranges::sentinel_t<decltype(v)>;
using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
static_assert(EqualComparable<Iter, Sentinel>);
static_assert(!EqualComparable<ConstIter, Sentinel>);
static_assert(!EqualComparable<Iter, ConstSentinel>);
static_assert(EqualComparable<ConstIter, ConstSentinel>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,235 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// template <bool OtherConst>
// requires(sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>,
// iterator_t<maybe-const<OtherConst, Views>>>&&...)
// friend constexpr common_type_t<range_difference_t<maybe-const<OtherConst, Views>>...>
// operator-(const iterator<OtherConst>&, const sentinel&)
//
// template <bool OtherConst>
// requires(sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>,
// iterator_t<maybe-const<OtherConst, Views>>>&&...)
// friend constexpr common_type_t<range_difference_t<maybe-const<OtherConst, Views>>...>
// operator-(const sentinel&, const iterator<OtherConst>&)
#include <cassert>
#include <concepts>
#include <functional>
#include <ranges>
#include <tuple>
#include "../types.h"
template <class Base = int*>
struct convertible_forward_sized_iterator {
Base it_ = nullptr;
using iterator_category = std::forward_iterator_tag;
using value_type = int;
using difference_type = intptr_t;
convertible_forward_sized_iterator() = default;
constexpr convertible_forward_sized_iterator(Base it) : it_(it) {}
template <std::convertible_to<Base> U>
constexpr convertible_forward_sized_iterator(const convertible_forward_sized_iterator<U>& it) : it_(it.it_) {}
constexpr decltype(*Base{}) operator*() const { return *it_; }
constexpr convertible_forward_sized_iterator& operator++() {
++it_;
return *this;
}
constexpr convertible_forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
friend constexpr bool operator==(const convertible_forward_sized_iterator&,
const convertible_forward_sized_iterator&) = default;
friend constexpr difference_type operator-(const convertible_forward_sized_iterator& x,
const convertible_forward_sized_iterator& y) {
return x.it_ - y.it_;
}
};
static_assert(std::forward_iterator<convertible_forward_sized_iterator<>>);
template <class Base>
struct convertible_sized_sentinel {
Base base_;
explicit convertible_sized_sentinel() = default;
constexpr convertible_sized_sentinel(const Base& it) : base_(it) {}
template <std::convertible_to<Base> U>
constexpr convertible_sized_sentinel(const convertible_sized_sentinel<U>& other) : base_(other.base_) {}
template <class U>
requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
friend constexpr bool operator==(const convertible_sized_sentinel& s, const U& base) {
return s.base_ == base;
}
template <class U>
requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
friend constexpr auto operator-(const convertible_sized_sentinel& s, const U& i) {
return s.base_ - i;
}
template <class U>
requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
friend constexpr auto operator-(const U& i, const convertible_sized_sentinel& s) {
return i - s.base_;
}
};
static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<>>,
convertible_forward_sized_iterator<>>);
static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<const int*>>,
convertible_forward_sized_iterator<int*>>);
static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<int*>>,
convertible_forward_sized_iterator<const int*>>);
struct ConstCompatibleForwardSized : IntBufferView {
using IntBufferView::IntBufferView;
using iterator = convertible_forward_sized_iterator<int*>;
using const_iterator = convertible_forward_sized_iterator<const int*>;
constexpr iterator begin() { return {buffer_}; }
constexpr const_iterator begin() const { return {buffer_}; }
constexpr convertible_sized_sentinel<iterator> end() { return iterator{buffer_ + size_}; }
constexpr convertible_sized_sentinel<const_iterator> end() const { return const_iterator{buffer_ + size_}; }
};
// clang-format off
template <class T, class U>
concept HasMinus = std::invocable<std::minus<>,const T&, const U&>;
template <class T>
concept SentinelHasMinus = HasMinus<std::ranges::sentinel_t<T>, std::ranges::iterator_t<T>>;
// clang-format on
constexpr bool test() {
int buffer1[5] = {1, 2, 3, 4, 5};
{
// simple-view
std::ranges::zip_view v{ForwardSizedNonCommon(buffer1)};
static_assert(!std::ranges::common_range<decltype(v)>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<decltype(v)>);
auto it = v.begin();
auto st = v.end();
assert(st - it == 5);
assert(st - std::ranges::next(it, 1) == 4);
assert(it - st == -5);
assert(std::ranges::next(it, 1) - st == -4);
static_assert(SentinelHasMinus<decltype(v)>);
}
{
// shortest range
std::ranges::zip_view v(std::views::iota(0, 3), ForwardSizedNonCommon(buffer1));
static_assert(!std::ranges::common_range<decltype(v)>);
auto it = v.begin();
auto st = v.end();
assert(st - it == 3);
assert(st - std::ranges::next(it, 1) == 2);
assert(it - st == -3);
assert(std::ranges::next(it, 1) - st == -2);
static_assert(SentinelHasMinus<decltype(v)>);
}
{
// underlying sentinel does not model sized_sentinel_for
std::ranges::zip_view v(std::views::iota(0), SizedRandomAccessView(buffer1));
static_assert(!std::ranges::common_range<decltype(v)>);
static_assert(!SentinelHasMinus<decltype(v)>);
}
{
// const imcompatible:
// underlying const sentinels cannot substract underlying iterators
// underlying sentinels cannot substract underlying const iterators
std::ranges::zip_view v(NonSimpleForwardSizedNonCommon{buffer1});
static_assert(!std::ranges::common_range<decltype(v)>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<decltype(v)>);
using Iter = std::ranges::iterator_t<decltype(v)>;
using ConstIter = std::ranges::iterator_t<const decltype(v)>;
static_assert(!std::is_same_v<Iter, ConstIter>);
using Sentinel = std::ranges::sentinel_t<decltype(v)>;
using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
static_assert(HasMinus<Iter, Sentinel>);
static_assert(HasMinus<Sentinel, Iter>);
static_assert(HasMinus<ConstIter, ConstSentinel>);
static_assert(HasMinus<ConstSentinel, ConstIter>);
auto it = v.begin();
auto const_it = std::as_const(v).begin();
auto st = v.end();
auto const_st = std::as_const(v).end();
assert(it - st == -5);
assert(st - it == 5);
assert(const_it - const_st == -5);
assert(const_st - const_it == 5);
static_assert(!HasMinus<Iter, ConstSentinel>);
static_assert(!HasMinus<ConstSentinel, Iter>);
static_assert(!HasMinus<ConstIter, Sentinel>);
static_assert(!HasMinus<Sentinel, ConstIter>);
}
{
// const compatible allow non-const to const conversion
std::ranges::zip_view v(ConstCompatibleForwardSized{buffer1});
static_assert(!std::ranges::common_range<decltype(v)>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<decltype(v)>);
using Iter = std::ranges::iterator_t<decltype(v)>;
using ConstIter = std::ranges::iterator_t<const decltype(v)>;
static_assert(!std::is_same_v<Iter, ConstIter>);
using Sentinel = std::ranges::sentinel_t<decltype(v)>;
using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
static_assert(HasMinus<Iter, Sentinel>);
static_assert(HasMinus<Sentinel, Iter>);
static_assert(HasMinus<ConstIter, ConstSentinel>);
static_assert(HasMinus<ConstSentinel, ConstIter>);
static_assert(HasMinus<Iter, ConstSentinel>);
static_assert(HasMinus<ConstSentinel, Iter>);
static_assert(HasMinus<ConstIter, Sentinel>);
static_assert(HasMinus<Sentinel, ConstIter>);
auto it = v.begin();
auto const_it = std::as_const(v).begin();
auto st = v.end();
auto const_st = std::as_const(v).end();
assert(it - st == -5);
assert(st - it == 5);
assert(const_it - const_st == -5);
assert(const_st - const_it == 5);
assert(it - const_st == -5);
assert(const_st - it == 5);
assert(const_it - st == -5);
assert(st - const_it == 5);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,100 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
// UNSUPPORTED: libcpp-has-no-incomplete-ranges
// constexpr auto size() requires(sized_range<Views>&&...)
// constexpr auto size() const requires(sized_range<const Views>&&...)
#include <ranges>
#include <cassert>
#include <tuple>
#include <utility>
#include "test_iterators.h"
#include "types.h"
int buffer[] = {1, 2, 3, 4, 5, 6, 7, 8, 9};
struct View : std::ranges::view_base {
std::size_t size_ = 0;
constexpr View(std::size_t s) : size_(s) {}
constexpr auto begin() const { return buffer; }
constexpr auto end() const { return buffer + size_; }
};
struct SizedNonConst : std::ranges::view_base {
using iterator = forward_iterator<int*>;
std::size_t size_ = 0;
constexpr SizedNonConst(std::size_t s) : size_(s) {}
constexpr auto begin() const { return iterator{buffer}; }
constexpr auto end() const { return iterator{buffer + size_}; }
constexpr std::size_t size() { return size_; }
};
struct StrangeSizeView : std::ranges::view_base {
constexpr auto begin() const { return buffer; }
constexpr auto end() const { return buffer + 8; }
constexpr auto size() { return 5; }
constexpr auto size() const { return 6; }
};
constexpr bool test() {
{
// single range
std::ranges::zip_view v(View(8));
assert(v.size() == 8);
assert(std::as_const(v).size() == 8);
}
{
// multiple ranges same type
std::ranges::zip_view v(View(2), View(3));
assert(v.size() == 2);
assert(std::as_const(v).size() == 2);
}
{
// multiple ranges different types
std::ranges::zip_view v(std::views::iota(0, 500), View(3));
assert(v.size() == 3);
assert(std::as_const(v).size() == 3);
}
{
// const-view non-sized range
std::ranges::zip_view v(SizedNonConst(2), View(3));
assert(v.size() == 2);
static_assert(std::ranges::sized_range<decltype(v)>);
static_assert(!std::ranges::sized_range<decltype(std::as_const(v))>);
}
{
// const/non-const has different sizes
std::ranges::zip_view v(StrangeSizeView{});
assert(v.size() == 5);
assert(std::as_const(v).size() == 6);
}
{
// underlying range not sized
std::ranges::zip_view v(InputCommonView{buffer});
static_assert(!std::ranges::sized_range<decltype(v)>);
static_assert(!std::ranges::sized_range<decltype(std::as_const(v))>);
}
return true;
}
int main(int, char**) {
test();
static_assert(test());
return 0;
}

View File

@ -0,0 +1,459 @@
//===----------------------------------------------------------------------===//
//
// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
// See https://llvm.org/LICENSE.txt for license information.
// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
//
//===----------------------------------------------------------------------===//
#ifndef TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H
#define TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H
#include <ranges>
#include "test_macros.h"
#include "test_iterators.h"
#include "test_range.h"
#if TEST_STD_VER <= 20
# error "range.zip/types.h" can only be included in builds supporting C++20
#endif // TEST_STD_VER <= 20
template <class T>
struct BufferView : std::ranges::view_base {
T* buffer_;
std::size_t size_;
template <std::size_t N>
constexpr BufferView(T (&b)[N]) : buffer_(b), size_(N) {}
};
using IntBufferView = BufferView<int>;
template <bool Simple>
struct Common : IntBufferView {
using IntBufferView::IntBufferView;
constexpr int* begin()
requires(!Simple)
{
return buffer_;
}
constexpr const int* begin() const { return buffer_; }
constexpr int* end()
requires(!Simple)
{
return buffer_ + size_;
}
constexpr const int* end() const { return buffer_ + size_; }
};
using SimpleCommon = Common<true>;
using NonSimpleCommon = Common<false>;
using SimpleCommonRandomAccessSized = SimpleCommon;
using NonSimpleCommonRandomAccessSized = NonSimpleCommon;
static_assert(std::ranges::common_range<Common<true>>);
static_assert(std::ranges::random_access_range<SimpleCommon>);
static_assert(std::ranges::sized_range<SimpleCommon>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleCommon>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleCommon>);
template <bool Simple>
struct CommonNonRandom : IntBufferView {
using IntBufferView::IntBufferView;
using const_iterator = forward_iterator<const int*>;
using iterator = forward_iterator<int*>;
constexpr iterator begin()
requires(!Simple) {
return iterator(buffer_);
}
constexpr const_iterator begin() const { return const_iterator(buffer_); }
constexpr iterator end()
requires(!Simple) {
return iterator(buffer_ + size_);
}
constexpr const_iterator end() const { return const_iterator(buffer_ + size_); }
};
using SimpleCommonNonRandom = CommonNonRandom<true>;
using NonSimpleCommonNonRandom = CommonNonRandom<false>;
static_assert(std::ranges::common_range<SimpleCommonNonRandom>);
static_assert(!std::ranges::random_access_range<SimpleCommonNonRandom>);
static_assert(!std::ranges::sized_range<SimpleCommonNonRandom>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleCommonNonRandom>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleCommonNonRandom>);
template <bool Simple>
struct NonCommon : IntBufferView {
using IntBufferView::IntBufferView;
constexpr int* begin()
requires(!Simple) {
return buffer_;
}
constexpr const int* begin() const { return buffer_; }
constexpr sentinel_wrapper<int*> end()
requires(!Simple) {
return sentinel_wrapper<int*>(buffer_ + size_);
}
constexpr sentinel_wrapper<const int*> end() const { return sentinel_wrapper<const int*>(buffer_ + size_); }
};
using SimpleNonCommon = NonCommon<true>;
using NonSimpleNonCommon = NonCommon<false>;
static_assert(!std::ranges::common_range<SimpleNonCommon>);
static_assert(std::ranges::random_access_range<SimpleNonCommon>);
static_assert(!std::ranges::sized_range<SimpleNonCommon>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleNonCommon>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommon>);
template <bool Simple>
struct NonCommonSized : IntBufferView {
using IntBufferView::IntBufferView;
constexpr int* begin()
requires(!Simple) {
return buffer_;
}
constexpr const int* begin() const { return buffer_; }
constexpr sentinel_wrapper<int*> end()
requires(!Simple) {
return sentinel_wrapper<int*>(buffer_ + size_);
}
constexpr sentinel_wrapper<const int*> end() const { return sentinel_wrapper<const int*>(buffer_ + size_); }
constexpr std::size_t size() const { return size_; }
};
using SimpleNonCommonSized = NonCommonSized<true>;
using SimpleNonCommonRandomAcessSized = SimpleNonCommonSized;
using NonSimpleNonCommonSized = NonCommonSized<false>;
using NonSimpleNonCommonRandomAcessSized = NonSimpleNonCommonSized;
static_assert(!std::ranges::common_range<SimpleNonCommonSized>);
static_assert(std::ranges::random_access_range<SimpleNonCommonSized>);
static_assert(std::ranges::sized_range<SimpleNonCommonSized>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleNonCommonSized>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommonSized>);
template <bool Simple>
struct NonCommonNonRandom : IntBufferView {
using IntBufferView::IntBufferView;
using const_iterator = forward_iterator<const int*>;
using iterator = forward_iterator<int*>;
constexpr iterator begin()
requires(!Simple) {
return iterator(buffer_);
}
constexpr const_iterator begin() const { return const_iterator(buffer_); }
constexpr sentinel_wrapper<iterator> end()
requires(!Simple) {
return sentinel_wrapper<iterator>(iterator(buffer_ + size_));
}
constexpr sentinel_wrapper<const_iterator> end() const {
return sentinel_wrapper<const_iterator>(const_iterator(buffer_ + size_));
}
};
using SimpleNonCommonNonRandom = NonCommonNonRandom<true>;
using NonSimpleNonCommonNonRandom = NonCommonNonRandom<false>;
static_assert(!std::ranges::common_range<SimpleNonCommonNonRandom>);
static_assert(!std::ranges::random_access_range<SimpleNonCommonNonRandom>);
static_assert(!std::ranges::sized_range<SimpleNonCommonNonRandom>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SimpleNonCommonNonRandom>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonCommonNonRandom>);
template <class Iter, class Sent = Iter, class NonConstIter = Iter, class NonConstSent = Sent>
struct BasicView : IntBufferView {
using IntBufferView::IntBufferView;
constexpr NonConstIter begin()
requires(!std::is_same_v<Iter, NonConstIter>) {
return NonConstIter(buffer_);
}
constexpr Iter begin() const { return Iter(buffer_); }
constexpr NonConstSent end()
requires(!std::is_same_v<Sent, NonConstSent>) {
if constexpr (std::is_same_v<NonConstIter, NonConstSent>) {
return NonConstIter(buffer_ + size_);
} else {
return NonConstSent(NonConstIter(buffer_ + size_));
}
}
constexpr Sent end() const {
if constexpr (std::is_same_v<Iter, Sent>) {
return Iter(buffer_ + size_);
} else {
return Sent(Iter(buffer_ + size_));
}
}
};
template <class Base = int*>
struct forward_sized_iterator {
Base it_ = nullptr;
using iterator_category = std::forward_iterator_tag;
using value_type = int;
using difference_type = intptr_t;
using pointer = Base;
using reference = decltype(*Base{});
forward_sized_iterator() = default;
constexpr forward_sized_iterator(Base it) : it_(it) {}
constexpr reference operator*() const { return *it_; }
constexpr forward_sized_iterator& operator++() {
++it_;
return *this;
}
constexpr forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
friend constexpr bool operator==(const forward_sized_iterator&, const forward_sized_iterator&) = default;
friend constexpr difference_type operator-(const forward_sized_iterator& x, const forward_sized_iterator& y) {
return x.it_ - y.it_;
}
};
static_assert(std::forward_iterator<forward_sized_iterator<>>);
static_assert(std::sized_sentinel_for<forward_sized_iterator<>, forward_sized_iterator<>>);
using ForwardSizedView = BasicView<forward_sized_iterator<>>;
static_assert(std::ranges::forward_range<ForwardSizedView>);
static_assert(std::ranges::sized_range<ForwardSizedView>);
static_assert(std::ranges::common_range<ForwardSizedView>);
static_assert(!std::ranges::random_access_range<ForwardSizedView>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<ForwardSizedView>);
using NonSimpleForwardSizedView = BasicView<forward_sized_iterator<const int*>, forward_sized_iterator<const int*>,
forward_sized_iterator<int*>, forward_sized_iterator<int*>>;
static_assert(std::ranges::forward_range<NonSimpleForwardSizedView>);
static_assert(std::ranges::sized_range<NonSimpleForwardSizedView>);
static_assert(std::ranges::common_range<NonSimpleForwardSizedView>);
static_assert(!std::ranges::random_access_range<NonSimpleForwardSizedView>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleForwardSizedView>);
using ForwardSizedNonCommon = BasicView<forward_sized_iterator<>, sized_sentinel<forward_sized_iterator<>>>;
static_assert(std::ranges::forward_range<ForwardSizedNonCommon>);
static_assert(std::ranges::sized_range<ForwardSizedNonCommon>);
static_assert(!std::ranges::common_range<ForwardSizedNonCommon>);
static_assert(!std::ranges::random_access_range<ForwardSizedNonCommon>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<ForwardSizedNonCommon>);
using NonSimpleForwardSizedNonCommon =
BasicView<forward_sized_iterator<const int*>, sized_sentinel<forward_sized_iterator<const int*>>,
forward_sized_iterator<int*>, sized_sentinel<forward_sized_iterator<int*>>>;
static_assert(std::ranges::forward_range<NonSimpleForwardSizedNonCommon>);
static_assert(std::ranges::sized_range<NonSimpleForwardSizedNonCommon>);
static_assert(!std::ranges::common_range<NonSimpleForwardSizedNonCommon>);
static_assert(!std::ranges::random_access_range<NonSimpleForwardSizedNonCommon>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleForwardSizedNonCommon>);
struct SizedRandomAccessView : IntBufferView {
using IntBufferView::IntBufferView;
using iterator = random_access_iterator<int*>;
constexpr auto begin() const { return iterator(buffer_); }
constexpr auto end() const { return sized_sentinel<iterator>(iterator(buffer_ + size_)); }
constexpr decltype(auto) operator[](std::size_t n) const { return *(begin() + n); }
};
static_assert(std::ranges::view<SizedRandomAccessView>);
static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
static_assert(std::ranges::sized_range<SizedRandomAccessView>);
using NonSizedRandomAccessView =
BasicView<random_access_iterator<int*>, sentinel_wrapper<random_access_iterator<int*>>>;
static_assert(!std::ranges::contiguous_range<NonSizedRandomAccessView>);
static_assert(std::ranges::random_access_range<SizedRandomAccessView>);
static_assert(!std::ranges::common_range<NonSizedRandomAccessView>);
static_assert(!std::ranges::sized_range<NonSizedRandomAccessView>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<NonSizedRandomAccessView>);
using NonSimpleNonSizedRandomAccessView =
BasicView<random_access_iterator<const int*>, sentinel_wrapper<random_access_iterator<const int*>>,
random_access_iterator<int*>, sentinel_wrapper<random_access_iterator<int*>> >;
static_assert(!std::ranges::contiguous_range<NonSimpleNonSizedRandomAccessView>);
static_assert(std::ranges::random_access_range<NonSimpleNonSizedRandomAccessView>);
static_assert(!std::ranges::common_range<NonSimpleNonSizedRandomAccessView>);
static_assert(!std::ranges::sized_range<NonSimpleNonSizedRandomAccessView>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleNonSizedRandomAccessView>);
using ContiguousCommonView = BasicView<int*>;
static_assert(std::ranges::contiguous_range<ContiguousCommonView>);
static_assert(std::ranges::common_range<ContiguousCommonView>);
static_assert(std::ranges::sized_range<ContiguousCommonView>);
using ContiguousNonCommonView = BasicView<int*, sentinel_wrapper<int*>>;
static_assert(std::ranges::contiguous_range<ContiguousNonCommonView>);
static_assert(!std::ranges::common_range<ContiguousNonCommonView>);
static_assert(!std::ranges::sized_range<ContiguousNonCommonView>);
using ContiguousNonCommonSized = BasicView<int*, sized_sentinel<int*>>;
static_assert(std::ranges::contiguous_range<ContiguousNonCommonSized>);
static_assert(!std::ranges::common_range<ContiguousNonCommonSized>);
static_assert(std::ranges::sized_range<ContiguousNonCommonSized>);
template <class Base = int*>
struct common_input_iterator {
Base it_;
using value_type = int;
using difference_type = std::intptr_t;
using iterator_concept = std::input_iterator_tag;
constexpr common_input_iterator() = default;
constexpr explicit common_input_iterator(Base it) : it_(it) {}
constexpr common_input_iterator& operator++() {
++it_;
return *this;
}
constexpr void operator++(int) { ++it_; }
constexpr int& operator*() const { return *it_; }
friend constexpr bool operator==(common_input_iterator const&, common_input_iterator const&) = default;
};
using InputCommonView = BasicView<common_input_iterator<>>;
static_assert(std::ranges::input_range<InputCommonView>);
static_assert(!std::ranges::forward_range<InputCommonView>);
static_assert(std::ranges::common_range<InputCommonView>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<InputCommonView>);
using NonSimpleInputCommonView = BasicView<common_input_iterator<const int*>, common_input_iterator<const int*>,
common_input_iterator<int*>, common_input_iterator<int*>>;
static_assert(std::ranges::input_range<NonSimpleInputCommonView>);
static_assert(!std::ranges::forward_range<NonSimpleInputCommonView>);
static_assert(std::ranges::common_range<NonSimpleInputCommonView>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleInputCommonView>);
using InputNonCommonView = BasicView<common_input_iterator<>, sentinel_wrapper<common_input_iterator<>>>;
static_assert(std::ranges::input_range<InputNonCommonView>);
static_assert(!std::ranges::forward_range<InputNonCommonView>);
static_assert(!std::ranges::common_range<InputNonCommonView>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<InputNonCommonView>);
using NonSimpleInputNonCommonView =
BasicView<common_input_iterator<const int*>, sentinel_wrapper<common_input_iterator<const int*>>,
common_input_iterator<int*>, sentinel_wrapper<common_input_iterator<int*>>>;
static_assert(std::ranges::input_range<InputNonCommonView>);
static_assert(!std::ranges::forward_range<InputNonCommonView>);
static_assert(!std::ranges::common_range<InputNonCommonView>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleInputNonCommonView>);
using BidiCommonView = BasicView<bidirectional_iterator<int*>>;
static_assert(!std::ranges::sized_range<BidiCommonView>);
static_assert(std::ranges::bidirectional_range<BidiCommonView>);
static_assert(!std::ranges::random_access_range<BidiCommonView>);
static_assert(std::ranges::common_range<BidiCommonView>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<BidiCommonView>);
using NonSimpleBidiCommonView = BasicView<bidirectional_iterator<const int*>, bidirectional_iterator<const int*>,
bidirectional_iterator<int*>, bidirectional_iterator<int*>>;
static_assert(!std::ranges::sized_range<NonSimpleBidiCommonView>);
static_assert(std::ranges::bidirectional_range<NonSimpleBidiCommonView>);
static_assert(!std::ranges::random_access_range<NonSimpleBidiCommonView>);
static_assert(std::ranges::common_range<NonSimpleBidiCommonView>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleBidiCommonView>);
struct SizedBidiCommon : BidiCommonView {
using BidiCommonView::BidiCommonView;
std::size_t size() const { return base(end()) - base(begin()); }
};
static_assert(std::ranges::sized_range<SizedBidiCommon>);
static_assert(std::ranges::bidirectional_range<SizedBidiCommon>);
static_assert(!std::ranges::random_access_range<SizedBidiCommon>);
static_assert(std::ranges::common_range<SizedBidiCommon>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SizedBidiCommon>);
struct NonSimpleSizedBidiCommon : NonSimpleBidiCommonView {
using NonSimpleBidiCommonView::NonSimpleBidiCommonView;
std::size_t size() const { return base(end()) - base(begin()); }
};
static_assert(std::ranges::sized_range<NonSimpleSizedBidiCommon>);
static_assert(std::ranges::bidirectional_range<NonSimpleSizedBidiCommon>);
static_assert(!std::ranges::random_access_range<NonSimpleSizedBidiCommon>);
static_assert(std::ranges::common_range<NonSimpleSizedBidiCommon>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleSizedBidiCommon>);
using BidiNonCommonView = BasicView<bidirectional_iterator<int*>, sentinel_wrapper<bidirectional_iterator<int*>>>;
static_assert(!std::ranges::sized_range<BidiNonCommonView>);
static_assert(std::ranges::bidirectional_range<BidiNonCommonView>);
static_assert(!std::ranges::random_access_range<BidiNonCommonView>);
static_assert(!std::ranges::common_range<BidiNonCommonView>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<BidiNonCommonView>);
using NonSimpleBidiNonCommonView =
BasicView<bidirectional_iterator<const int*>, sentinel_wrapper<bidirectional_iterator<const int*>>,
bidirectional_iterator<int*>, sentinel_wrapper<bidirectional_iterator<int*>>>;
static_assert(!std::ranges::sized_range<NonSimpleBidiNonCommonView>);
static_assert(std::ranges::bidirectional_range<NonSimpleBidiNonCommonView>);
static_assert(!std::ranges::random_access_range<NonSimpleBidiNonCommonView>);
static_assert(!std::ranges::common_range<NonSimpleBidiNonCommonView>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleBidiNonCommonView>);
using SizedBidiNonCommonView = BasicView<bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
static_assert(std::ranges::sized_range<SizedBidiNonCommonView>);
static_assert(std::ranges::bidirectional_range<SizedBidiNonCommonView>);
static_assert(!std::ranges::random_access_range<SizedBidiNonCommonView>);
static_assert(!std::ranges::common_range<SizedBidiNonCommonView>);
LIBCPP_STATIC_ASSERT(std::ranges::__simple_view<SizedBidiNonCommonView>);
using NonSimpleSizedBidiNonCommonView =
BasicView<bidirectional_iterator<const int*>, sized_sentinel<bidirectional_iterator<const int*>>,
bidirectional_iterator<int*>, sized_sentinel<bidirectional_iterator<int*>>>;
static_assert(std::ranges::sized_range<NonSimpleSizedBidiNonCommonView>);
static_assert(std::ranges::bidirectional_range<NonSimpleSizedBidiNonCommonView>);
static_assert(!std::ranges::random_access_range<NonSimpleSizedBidiNonCommonView>);
static_assert(!std::ranges::common_range<NonSimpleSizedBidiNonCommonView>);
LIBCPP_STATIC_ASSERT(!std::ranges::__simple_view<NonSimpleSizedBidiNonCommonView>);
namespace adltest{
struct iter_move_swap_iterator {
std::reference_wrapper<int> iter_move_called_times;
std::reference_wrapper<int> iter_swap_called_times;
int i = 0;
using iterator_category = std::input_iterator_tag;
using value_type = int;
using difference_type = intptr_t;
constexpr int operator*() const { return i; }
constexpr iter_move_swap_iterator& operator++() {
++i;
return *this;
}
constexpr void operator++(int) { ++i; }
friend constexpr bool operator==(const iter_move_swap_iterator& x, std::default_sentinel_t) { return x.i == 5; }
friend constexpr int iter_move(iter_move_swap_iterator const& it) {
++it.iter_move_called_times;
return it.i;
}
friend constexpr void iter_swap(iter_move_swap_iterator const& x, iter_move_swap_iterator const& y) {
++x.iter_swap_called_times;
++y.iter_swap_called_times;
}
};
struct IterMoveSwapRange {
int iter_move_called_times = 0;
int iter_swap_called_times = 0;
constexpr auto begin() { return iter_move_swap_iterator{iter_move_called_times, iter_swap_called_times}; }
constexpr auto end() const { return std::default_sentinel; }
};
} // namespace adltest
#endif // TEST_STD_RANGES_RANGE_ADAPTORS_RANGE_ZIP_TYPES_H