1//===----------------------------------------------------------------------===//
2//
3// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4// See https://llvm.org/LICENSE.txt for license information.
5// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6//
7//===----------------------------------------------------------------------===//
8
9// UNSUPPORTED: c++03, c++11, c++14, c++17, c++20
10
11// template <bool OtherConst>
12// requires(sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>,
13// iterator_t<maybe-const<OtherConst, Views>>>&&...)
14// friend constexpr common_type_t<range_difference_t<maybe-const<OtherConst, Views>>...>
15// operator-(const iterator<OtherConst>&, const sentinel&)
16//
17// template <bool OtherConst>
18// requires(sized_sentinel_for<sentinel_t<maybe-const<Const, Views>>,
19// iterator_t<maybe-const<OtherConst, Views>>>&&...)
20// friend constexpr common_type_t<range_difference_t<maybe-const<OtherConst, Views>>...>
21// operator-(const sentinel&, const iterator<OtherConst>&)
22
23#include <cassert>
24#include <concepts>
25#include <functional>
26#include <ranges>
27#include <tuple>
28
29#include "../types.h"
30
31template <class Base = int*>
32struct convertible_forward_sized_iterator {
33 Base it_ = nullptr;
34
35 using iterator_category = std::forward_iterator_tag;
36 using value_type = int;
37 using difference_type = std::intptr_t;
38
39 convertible_forward_sized_iterator() = default;
40 constexpr convertible_forward_sized_iterator(Base it) : it_(it) {}
41
42 template <std::convertible_to<Base> U>
43 constexpr convertible_forward_sized_iterator(const convertible_forward_sized_iterator<U>& it) : it_(it.it_) {}
44
45 constexpr decltype(*Base{}) operator*() const { return *it_; }
46
47 constexpr convertible_forward_sized_iterator& operator++() {
48 ++it_;
49 return *this;
50 }
51 constexpr convertible_forward_sized_iterator operator++(int) { return forward_sized_iterator(it_++); }
52
53 friend constexpr bool operator==(const convertible_forward_sized_iterator&,
54 const convertible_forward_sized_iterator&) = default;
55
56 friend constexpr difference_type operator-(const convertible_forward_sized_iterator& x,
57 const convertible_forward_sized_iterator& y) {
58 return x.it_ - y.it_;
59 }
60};
61static_assert(std::forward_iterator<convertible_forward_sized_iterator<>>);
62
63template <class Base>
64struct convertible_sized_sentinel {
65 Base base_;
66 explicit convertible_sized_sentinel() = default;
67 constexpr convertible_sized_sentinel(const Base& it) : base_(it) {}
68
69 template <std::convertible_to<Base> U>
70 constexpr convertible_sized_sentinel(const convertible_sized_sentinel<U>& other) : base_(other.base_) {}
71
72 template <class U>
73 requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
74 friend constexpr bool operator==(const convertible_sized_sentinel& s, const U& base) {
75 return s.base_ == base;
76 }
77 template <class U>
78 requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
79 friend constexpr auto operator-(const convertible_sized_sentinel& s, const U& i) {
80 return s.base_ - i;
81 }
82
83 template <class U>
84 requires(std::convertible_to<Base, U> || std::convertible_to<U, Base>)
85 friend constexpr auto operator-(const U& i, const convertible_sized_sentinel& s) {
86 return i - s.base_;
87 }
88};
89static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<>>,
90 convertible_forward_sized_iterator<>>);
91static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<const int*>>,
92 convertible_forward_sized_iterator<int*>>);
93static_assert(std::sized_sentinel_for<convertible_sized_sentinel<convertible_forward_sized_iterator<int*>>,
94 convertible_forward_sized_iterator<const int*>>);
95
96struct ConstCompatibleForwardSized : IntBufferView {
97 using IntBufferView::IntBufferView;
98
99 using iterator = convertible_forward_sized_iterator<int*>;
100 using const_iterator = convertible_forward_sized_iterator<const int*>;
101
102 constexpr iterator begin() { return {buffer_}; }
103 constexpr const_iterator begin() const { return {buffer_}; }
104 constexpr convertible_sized_sentinel<iterator> end() { return iterator{buffer_ + size_}; }
105 constexpr convertible_sized_sentinel<const_iterator> end() const { return const_iterator{buffer_ + size_}; }
106};
107
108// clang-format off
109template <class T, class U>
110concept HasMinus = std::invocable<std::minus<>,const T&, const U&>;
111
112template <class T>
113concept SentinelHasMinus = HasMinus<std::ranges::sentinel_t<T>, std::ranges::iterator_t<T>>;
114// clang-format on
115
116constexpr bool test() {
117 int buffer1[5] = {1, 2, 3, 4, 5};
118
119 {
120 // simple-view
121 std::ranges::zip_view v{ForwardSizedNonCommon(buffer1)};
122 static_assert(!std::ranges::common_range<decltype(v)>);
123 static_assert(simple_view<decltype(v)>);
124
125 auto it = v.begin();
126 auto st = v.end();
127 assert(st - it == 5);
128 assert(st - std::ranges::next(it, 1) == 4);
129
130 assert(it - st == -5);
131 assert(std::ranges::next(it, 1) - st == -4);
132 static_assert(SentinelHasMinus<decltype(v)>);
133 }
134
135 {
136 // shortest range
137 std::ranges::zip_view v(std::views::iota(0, 3), ForwardSizedNonCommon(buffer1));
138 static_assert(!std::ranges::common_range<decltype(v)>);
139 auto it = v.begin();
140 auto st = v.end();
141 assert(st - it == 3);
142 assert(st - std::ranges::next(it, 1) == 2);
143
144 assert(it - st == -3);
145 assert(std::ranges::next(it, 1) - st == -2);
146 static_assert(SentinelHasMinus<decltype(v)>);
147 }
148
149 {
150 // underlying sentinel does not model sized_sentinel_for
151 std::ranges::zip_view v(std::views::iota(0), SizedRandomAccessView(buffer1));
152 static_assert(!std::ranges::common_range<decltype(v)>);
153 static_assert(!SentinelHasMinus<decltype(v)>);
154 }
155
156 {
157 // const incompatible:
158 // underlying const sentinels cannot subtract underlying iterators
159 // underlying sentinels cannot subtract underlying const iterators
160 std::ranges::zip_view v(NonSimpleForwardSizedNonCommon{buffer1});
161 static_assert(!std::ranges::common_range<decltype(v)>);
162 static_assert(!simple_view<decltype(v)>);
163
164 using Iter = std::ranges::iterator_t<decltype(v)>;
165 using ConstIter = std::ranges::iterator_t<const decltype(v)>;
166 static_assert(!std::is_same_v<Iter, ConstIter>);
167 using Sentinel = std::ranges::sentinel_t<decltype(v)>;
168 using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
169 static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
170
171 static_assert(HasMinus<Iter, Sentinel>);
172 static_assert(HasMinus<Sentinel, Iter>);
173 static_assert(HasMinus<ConstIter, ConstSentinel>);
174 static_assert(HasMinus<ConstSentinel, ConstIter>);
175 auto it = v.begin();
176 auto const_it = std::as_const(v).begin();
177 auto st = v.end();
178 auto const_st = std::as_const(v).end();
179 assert(it - st == -5);
180 assert(st - it == 5);
181 assert(const_it - const_st == -5);
182 assert(const_st - const_it == 5);
183
184 static_assert(!HasMinus<Iter, ConstSentinel>);
185 static_assert(!HasMinus<ConstSentinel, Iter>);
186 static_assert(!HasMinus<ConstIter, Sentinel>);
187 static_assert(!HasMinus<Sentinel, ConstIter>);
188 }
189
190 {
191 // const compatible allow non-const to const conversion
192 std::ranges::zip_view v(ConstCompatibleForwardSized{buffer1});
193 static_assert(!std::ranges::common_range<decltype(v)>);
194 static_assert(!simple_view<decltype(v)>);
195
196 using Iter = std::ranges::iterator_t<decltype(v)>;
197 using ConstIter = std::ranges::iterator_t<const decltype(v)>;
198 static_assert(!std::is_same_v<Iter, ConstIter>);
199 using Sentinel = std::ranges::sentinel_t<decltype(v)>;
200 using ConstSentinel = std::ranges::sentinel_t<const decltype(v)>;
201 static_assert(!std::is_same_v<Sentinel, ConstSentinel>);
202
203 static_assert(HasMinus<Iter, Sentinel>);
204 static_assert(HasMinus<Sentinel, Iter>);
205 static_assert(HasMinus<ConstIter, ConstSentinel>);
206 static_assert(HasMinus<ConstSentinel, ConstIter>);
207 static_assert(HasMinus<Iter, ConstSentinel>);
208 static_assert(HasMinus<ConstSentinel, Iter>);
209 static_assert(HasMinus<ConstIter, Sentinel>);
210 static_assert(HasMinus<Sentinel, ConstIter>);
211
212 auto it = v.begin();
213 auto const_it = std::as_const(v).begin();
214 auto st = v.end();
215 auto const_st = std::as_const(v).end();
216
217 assert(it - st == -5);
218 assert(st - it == 5);
219 assert(const_it - const_st == -5);
220 assert(const_st - const_it == 5);
221 assert(it - const_st == -5);
222 assert(const_st - it == 5);
223 assert(const_it - st == -5);
224 assert(st - const_it == 5);
225 }
226 return true;
227}
228
229int main(int, char**) {
230 test();
231 static_assert(test());
232
233 return 0;
234}
235

source code of libcxx/test/std/ranges/range.adaptors/range.zip/sentinel/minus.pass.cpp