@@ -88,7 +88,11 @@ constexpr void check_forward_sized_sentinel(int* first, int* last, std::iter_dif
88
88
89
89
template <typename It>
90
90
constexpr void check_backward (int * first, int * last, std::iter_difference_t <It> n, int * expected) {
91
- static_assert (std::random_access_iterator<It>, " This test doesn't support non random access iterators" );
91
+ // Check preconditions for `advance` when called with negative `n`:
92
+ // <https://eel.is/c++draft/iterators#range.iter.op.advance-5>
93
+ assert (n < 0 );
94
+ static_assert (std::bidirectional_iterator<It>);
95
+
92
96
using Difference = std::iter_difference_t <It>;
93
97
Difference const M = (expected - last); // expected travel distance (which is negative)
94
98
@@ -104,10 +108,34 @@ constexpr void check_backward(int* first, int* last, std::iter_difference_t<It>
104
108
{
105
109
auto it = stride_counting_iterator (It (last));
106
110
auto sent = stride_counting_iterator (It (first));
111
+ static_assert (std::bidirectional_iterator<stride_counting_iterator<It>>);
112
+
107
113
(void )std::ranges::advance (it, n, sent);
108
- assert (it.stride_count () <= 1 );
109
- assert (it.stride_displacement () <= 1 );
110
- assert (it.equals_count () == 0 );
114
+
115
+ if constexpr (std::sized_sentinel_for<It, It>) {
116
+ if (expected == first) {
117
+ // In this case, the algorithm can just do `it = std::move(sent);`
118
+ // instead of doing iterator arithmetic:
119
+ // <https://eel.is/c++draft/iterators#range.iter.op.advance-4.1>
120
+ assert (it.stride_count () == 0 );
121
+ assert (it.stride_displacement () == 0 );
122
+ } else {
123
+ assert (it.stride_count () == 1 );
124
+ assert (it.stride_displacement () == 1 );
125
+ }
126
+ assert (it.equals_count () == 0 );
127
+ } else {
128
+ assert (it.stride_count () == -M);
129
+ assert (it.stride_displacement () == M);
130
+ if (-n > -M) {
131
+ // We "hit" the bound, so there is one extra equality check.
132
+ assert (it.equals_count () == -M + 1 );
133
+ } else {
134
+ assert (it.equals_count () == -M);
135
+ }
136
+ // In any case, there must not be more than `-n` bounds checks.
137
+ assert (it.equals_count () <= -n);
138
+ }
111
139
}
112
140
}
113
141
@@ -201,11 +229,12 @@ constexpr bool test() {
201
229
check_forward_sized_sentinel<int *>( range, range+size, n, expected);
202
230
}
203
231
204
- {
205
- // Note that we can only test ranges::advance with a negative n for iterators that
206
- // are sized sentinels for themselves, because ranges::advance is UB otherwise .
207
- // In particular, that excludes bidirectional_iterators since those are not sized sentinels.
232
+ // Exclude the `n == 0` case for the backwards checks.
233
+ // Input and forward iterators are not tested as the backwards case does
234
+ // not apply for them .
235
+ if (n > 0 ) {
208
236
int * expected = n > size ? range : range + size - n;
237
+ check_backward<bidirectional_iterator<int *>>(range, range+size, -n, expected);
209
238
check_backward<random_access_iterator<int *>>(range, range+size, -n, expected);
210
239
check_backward<contiguous_iterator<int *>>( range, range+size, -n, expected);
211
240
check_backward<int *>( range, range+size, -n, expected);
@@ -226,20 +255,6 @@ constexpr bool test() {
226
255
assert (i == iota_iterator{INT_MIN+1 });
227
256
}
228
257
229
- // Check that we don't do an unneeded bounds check when decrementing a
230
- // `bidirectional_iterator` that doesn't model `sized_sentinel_for`.
231
- {
232
- static_assert (std::bidirectional_iterator<bidirectional_iterator<iota_iterator>>);
233
- static_assert (
234
- !std::sized_sentinel_for<bidirectional_iterator<iota_iterator>, bidirectional_iterator<iota_iterator>>);
235
-
236
- auto it = stride_counting_iterator (bidirectional_iterator (iota_iterator{+1 }));
237
- auto sent = stride_counting_iterator (bidirectional_iterator (iota_iterator{-2 }));
238
- assert (std::ranges::advance (it, -3 , sent) == 0 );
239
- assert (base (base (it)) == iota_iterator{-2 });
240
- assert (it.equals_count () == 3 );
241
- }
242
-
243
258
return true ;
244
259
}
245
260
0 commit comments