From Jason Turner

[range.join.with]

Diff to HTML by rtfpessoa

Files changed (1) hide show
  1. tmp/tmpkp4yspkt/{from.md → to.md} +524 -0
tmp/tmpkp4yspkt/{from.md → to.md} RENAMED
@@ -0,0 +1,524 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ ### Join with view <a id="range.join.with">[[range.join.with]]</a>
2
+
3
+ #### Overview <a id="range.join.with.overview">[[range.join.with.overview]]</a>
4
+
5
+ `join_with_view` takes a view and a delimiter, and flattens the view,
6
+ inserting every element of the delimiter in between elements of the
7
+ view. The delimiter can be a single element or a view of elements.
8
+
9
+ The name `views::join_with` denotes a range adaptor object
10
+ [[range.adaptor.object]]. Given subexpressions `E` and `F`, the
11
+ expression `views::join_with(E, F)` is expression-equivalent to
12
+ `join_with_view(E, F)`.
13
+
14
+ [*Example 1*:
15
+
16
+ ``` cpp
17
+ vector<string> vs = {"the", "quick", "brown", "fox"};
18
+ for (char c : vs | views::join_with('-')) {
19
+ cout << c;
20
+ }
21
+ // The above prints the-quick-brown-fox
22
+ ```
23
+
24
+ — *end example*]
25
+
26
+ #### Class template `join_with_view` <a id="range.join.with.view">[[range.join.with.view]]</a>
27
+
28
+ ``` cpp
29
+ namespace std::ranges {
30
+ template<class R, class P>
31
+ concept compatible-joinable-ranges = // exposition only
32
+ common_with<range_value_t<R>, range_value_t<P>> &&
33
+ common_reference_with<range_reference_t<R>, range_reference_t<P>> &&
34
+ common_reference_with<range_rvalue_reference_t<R>, range_rvalue_reference_t<P>>;
35
+
36
+ template<class R>
37
+ concept bidirectional-common = bidirectional_range<R> && common_range<R>; // exposition only
38
+
39
+ template<input_range V, forward_range Pattern>
40
+ requires view<V> && input_range<range_reference_t<V>>
41
+ && view<Pattern>
42
+ && compatible-joinable-ranges<range_reference_t<V>, Pattern>
43
+ class join_with_view : public view_interface<join_with_view<V, Pattern>> {
44
+ using InnerRng = range_reference_t<V>; // exposition only
45
+
46
+ V base_ = V(); // exposition only
47
+ non-propagating-cache<iterator_t<V>> outer_it_; // exposition only, present only
48
+ // when !forward_range<V>
49
+ non-propagating-cache<remove_cv_t<InnerRng>> inner_; // exposition only, present only
50
+ // if is_reference_v<InnerRng> is false
51
+ Pattern pattern_ = Pattern(); // exposition only
52
+
53
+ // [range.join.with.iterator], class template join_with_view::iterator
54
+ template<bool Const> struct iterator; // exposition only
55
+
56
+ // [range.join.with.sentinel], class template join_with_view::sentinel
57
+ template<bool Const> struct sentinel; // exposition only
58
+
59
+ public:
60
+ join_with_view()
61
+ requires default_initializable<V> && default_initializable<Pattern> = default;
62
+
63
+ constexpr explicit join_with_view(V base, Pattern pattern);
64
+
65
+ template<input_range R>
66
+ requires constructible_from<V, views::all_t<R>> &&
67
+ constructible_from<Pattern, single_view<range_value_t<InnerRng>>>
68
+ constexpr explicit join_with_view(R&& r, range_value_t<InnerRng> e);
69
+
70
+ constexpr V base() const & requires copy_constructible<V> { return base_; }
71
+ constexpr V base() && { return std::move(base_); }
72
+
73
+ constexpr auto begin() {
74
+ if constexpr (forward_range<V>) {
75
+ constexpr bool use_const =
76
+ simple-view<V> && is_reference_v<InnerRng> && simple-view<Pattern>;
77
+ return iterator<use_const>{*this, ranges::begin(base_)};
78
+ }
79
+ else {
80
+ outer_it_ = ranges::begin(base_);
81
+ return iterator<false>{*this};
82
+ }
83
+ }
84
+ constexpr auto begin() const
85
+ requires forward_range<const V> &&
86
+ forward_range<const Pattern> &&
87
+ is_reference_v<range_reference_t<const V>> &&
88
+ input_range<range_reference_t<const V>> {
89
+ return iterator<true>{*this, ranges::begin(base_)};
90
+ }
91
+
92
+ constexpr auto end() {
93
+ if constexpr (forward_range<V> &&
94
+ is_reference_v<InnerRng> && forward_range<InnerRng> &&
95
+ common_range<V> && common_range<InnerRng>)
96
+ return iterator<simple-view<V> && simple-view<Pattern>>{*this, ranges::end(base_)};
97
+ else
98
+ return sentinel<simple-view<V> && simple-view<Pattern>>{*this};
99
+ }
100
+ constexpr auto end() const
101
+ requires forward_range<const V> && forward_range<const Pattern> &&
102
+ is_reference_v<range_reference_t<const V>> &&
103
+ input_range<range_reference_t<const V>> {
104
+ using InnerConstRng = range_reference_t<const V>;
105
+ if constexpr (forward_range<InnerConstRng> &&
106
+ common_range<const V> && common_range<InnerConstRng>)
107
+ return iterator<true>{*this, ranges::end(base_)};
108
+ else
109
+ return sentinel<true>{*this};
110
+ }
111
+ };
112
+
113
+ template<class R, class P>
114
+ join_with_view(R&&, P&&) -> join_with_view<views::all_t<R>, views::all_t<P>>;
115
+
116
+ template<input_range R>
117
+ join_with_view(R&&, range_value_t<range_reference_t<R>>)
118
+ -> join_with_view<views::all_t<R>, single_view<range_value_t<range_reference_t<R>>>>;
119
+ }
120
+ ```
121
+
122
+ ``` cpp
123
+ constexpr explicit join_with_view(V base, Pattern pattern);
124
+ ```
125
+
126
+ *Effects:* Initializes *base\_* with `std::move(base)` and *pattern\_*
127
+ with `std::move(pattern)`.
128
+
129
+ ``` cpp
130
+ template<input_range R>
131
+ requires constructible_from<V, views::all_t<R>> &&
132
+ constructible_from<Pattern, single_view<range_value_t<InnerRng>>>
133
+ constexpr explicit join_with_view(R&& r, range_value_t<InnerRng> e);
134
+ ```
135
+
136
+ *Effects:* Initializes *base\_* with `views::all(std::forward<R>(r))`
137
+ and *pattern\_* with `views::single(std::move(e))`.
138
+
139
+ #### Class template `join_with_view::iterator` <a id="range.join.with.iterator">[[range.join.with.iterator]]</a>
140
+
141
+ ``` cpp
142
+ namespace std::ranges {
143
+ template<input_range V, forward_range Pattern>
144
+ requires view<V> && input_range<range_reference_t<V>>
145
+ && view<Pattern> && compatible-joinable-ranges<range_reference_t<V>, Pattern>
146
+ template<bool Const>
147
+ class join_with_view<V, Pattern>::iterator {
148
+ using Parent = maybe-const<Const, join_with_view>; // exposition only
149
+ using Base = maybe-const<Const, V>; // exposition only
150
+ using InnerBase = range_reference_t<Base>; // exposition only
151
+ using PatternBase = maybe-const<Const, Pattern>; // exposition only
152
+
153
+ using OuterIter = iterator_t<Base>; // exposition only
154
+ using InnerIter = iterator_t<InnerBase>; // exposition only
155
+ using PatternIter = iterator_t<PatternBase>; // exposition only
156
+
157
+ static constexpr bool ref-is-glvalue = is_reference_v<InnerBase>; // exposition only
158
+
159
+ Parent* parent_ = nullptr; // exposition only
160
+ OuterIter outer_it_ = OuterIter(); // exposition only, present only
161
+ // if Base models forward_range
162
+ variant<PatternIter, InnerIter> inner_it_; // exposition only
163
+
164
+ constexpr iterator(Parent& parent, OuterIter outer)
165
+ requires forward_range<Base>; // exposition only
166
+ constexpr explicit iterator(Parent& parent)
167
+ requires (!forward_range<Base>); // exposition only
168
+ constexpr OuterIter& outer(); // exposition only
169
+ constexpr const OuterIter& outer() const; // exposition only
170
+ constexpr auto& update-inner(); // exposition only
171
+ constexpr auto& get-inner(); // exposition only
172
+ constexpr void satisfy(); // exposition only
173
+
174
+ public:
175
+ using iterator_concept = see below;
176
+ using iterator_category = see below; // not always present
177
+ using value_type = see below;
178
+ using difference_type = see below;
179
+
180
+ iterator() = default;
181
+ constexpr iterator(iterator<!Const> i)
182
+ requires Const && convertible_to<iterator_t<V>, OuterIter> &&
183
+ convertible_to<iterator_t<InnerRng>, InnerIter> &&
184
+ convertible_to<iterator_t<Pattern>, PatternIter>;
185
+
186
+ constexpr decltype(auto) operator*() const;
187
+
188
+ constexpr iterator& operator++();
189
+ constexpr void operator++(int);
190
+ constexpr iterator operator++(int)
191
+ requires ref-is-glvalue && forward_iterator<OuterIter> &&
192
+ forward_iterator<InnerIter>;
193
+
194
+ constexpr iterator& operator--()
195
+ requires ref-is-glvalue && bidirectional_range<Base> &&
196
+ bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;
197
+ constexpr iterator operator--(int)
198
+ requires ref-is-glvalue && bidirectional_range<Base> &&
199
+ bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;
200
+
201
+ friend constexpr bool operator==(const iterator& x, const iterator& y)
202
+ requires ref-is-glvalue && forward_range<Base> &&
203
+ equality_comparable<InnerIter>;
204
+
205
+ friend constexpr decltype(auto) iter_move(const iterator& x) {
206
+ using rvalue_reference = common_reference_t<
207
+ iter_rvalue_reference_t<InnerIter>,
208
+ iter_rvalue_reference_t<PatternIter>>;
209
+ return visit<rvalue_reference>(ranges::iter_move, x.inner_it_);
210
+ }
211
+
212
+ friend constexpr void iter_swap(const iterator& x, const iterator& y)
213
+ requires indirectly_swappable<InnerIter, PatternIter> {
214
+ visit(ranges::iter_swap, x.inner_it_, y.inner_it_);
215
+ }
216
+ };
217
+ }
218
+ ```
219
+
220
+ `iterator::iterator_concept` is defined as follows:
221
+
222
+ - If *`ref-is-glvalue`* is `true`, *`Base`* models
223
+ `bidirectional_range`, and *`InnerBase`* and *`PatternBase`* each
224
+ model `bidirectional-common`, then `iterator_concept` denotes
225
+ `bidirectional_iterator_tag`.
226
+ - Otherwise, if *`ref-is-glvalue`* is `true` and *`Base`* and
227
+ *`InnerBase`* each model `forward_range`, then `iterator_concept`
228
+ denotes `forward_iterator_tag`.
229
+ - Otherwise, `iterator_concept` denotes `input_iterator_tag`.
230
+
231
+ The member *typedef-name* `iterator_category` is defined if and only if
232
+ *`ref-is-glvalue`* is `true`, and *`Base`* and *`InnerBase`* each model
233
+ `forward_range`. In that case, `iterator::iterator_category` is defined
234
+ as follows:
235
+
236
+ - Let *OUTERC* denote `iterator_traits<OuterIter>::iterator_category`,
237
+ let *INNERC* denote `iterator_traits<InnerIter>::iterator_category`,
238
+ and let *PATTERNC* denote
239
+ `iterator_-traits<PatternIter>::iterator_category`.
240
+ - If
241
+ ``` cpp
242
+ is_reference_v<common_reference_t<iter_reference_t<InnerIter>,
243
+ iter_reference_t<PatternIter>>>
244
+ ```
245
+
246
+ is `false`, `iterator_category` denotes `input_iterator_tag`.
247
+ - Otherwise, if *OUTERC*, *INNERC*, and *PATTERNC* each model
248
+ `derived_from<bidirectional_iterator_category>` and *`InnerBase`* and
249
+ *`PatternBase`* each model `common_range`, `iterator_category` denotes
250
+ `bidirectional_iterator_tag`.
251
+ - Otherwise, if *OUTERC*, *INNERC*, and *PATTERNC* each model
252
+ `derived_from<forward_iterator_tag>`, `iterator_category` denotes
253
+ `forward_iterator_tag`.
254
+ - Otherwise, `iterator_category` denotes `input_iterator_tag`.
255
+
256
+ `iterator::value_type` denotes the type:
257
+
258
+ ``` cpp
259
+ common_type_t<iter_value_t<InnerIter>, iter_value_t<PatternIter>>
260
+ ```
261
+
262
+ `iterator::difference_type` denotes the type:
263
+
264
+ ``` cpp
265
+ common_type_t<
266
+ iter_difference_t<OuterIter>,
267
+ iter_difference_t<InnerIter>,
268
+ iter_difference_t<PatternIter>>
269
+ ```
270
+
271
+ ``` cpp
272
+ constexpr OuterIter& outer();
273
+ constexpr const OuterIter& outer() const;
274
+ ```
275
+
276
+ *Returns:* *outer_it\_* if *Base* models `forward_range`; otherwise,
277
+ `*`*`parent_`*`->`*`outer_it_`*.
278
+
279
+ ``` cpp
280
+ constexpr auto& update-inner();
281
+ ```
282
+
283
+ *Effects:* Equivalent to:
284
+
285
+ ``` cpp
286
+ if constexpr (ref-is-glvalue)
287
+ return as-lvalue(*outer());
288
+ else
289
+ return parent_->inner_.emplace-deref(outer());
290
+ ```
291
+
292
+ ``` cpp
293
+ constexpr auto& get-inner();
294
+ ```
295
+
296
+ *Effects:* Equivalent to:
297
+
298
+ ``` cpp
299
+ if constexpr (ref-is-glvalue)
300
+ return as-lvalue(*outer());
301
+ else
302
+ return *parent_->inner_;
303
+ ```
304
+
305
+ ``` cpp
306
+ constexpr void satisfy();
307
+ ```
308
+
309
+ *Effects:* Equivalent to:
310
+
311
+ ``` cpp
312
+ while (true) {
313
+ if (inner_it_.index() == 0) {
314
+ if (std::get<0>(inner_it_) != ranges::end(parent_->pattern_))
315
+ break;
316
+ inner_it_.emplace<1>(ranges::begin(update-inner()));
317
+ } else {
318
+ if (std::get<1>(inner_it_) != ranges::end(get-inner()))
319
+ break;
320
+ if (++outer() == ranges::end(parent_->base_)) {
321
+ if constexpr (ref-is-glvalue)
322
+ inner_it_.emplace<0>();
323
+ break;
324
+ }
325
+ inner_it_.emplace<0>(ranges::begin(parent_->pattern_));
326
+ }
327
+ }
328
+ ```
329
+
330
+ [*Note 1*: `join_with_view` iterators use the *satisfy* function to
331
+ skip over empty inner ranges. — *end note*]
332
+
333
+ ``` cpp
334
+ constexpr iterator(Parent& parent, OuterIter outer)
335
+ requires forward_range<Base>;
336
+ constexpr explicit iterator(Parent& parent)
337
+ requires (!forward_range<Base>);
338
+ ```
339
+
340
+ *Effects:* Initializes *parent\_* with `addressof(parent)`. For the
341
+ first overload, also initializes *outer_it\_* with `std::move(outer)`.
342
+ Then, equivalent to:
343
+
344
+ ``` cpp
345
+ if (outer() != ranges::end(parent_->base_)) {
346
+ inner_it_.emplace<1>(ranges::begin(update-inner()));
347
+ satisfy();
348
+ }
349
+ ```
350
+
351
+ ``` cpp
352
+ constexpr iterator(iterator<!Const> i)
353
+ requires Const && convertible_to<iterator_t<V>, OuterIter> &&
354
+ convertible_to<iterator_t<InnerRng>, InnerIter> &&
355
+ convertible_to<iterator_t<Pattern>, PatternIter>;
356
+ ```
357
+
358
+ *Effects:* Initializes *outer_it\_* with `std::move(i.`*`outer_it_`*`)`
359
+ and *parent\_* with `i.`*`parent_`*. Then, equivalent to:
360
+
361
+ ``` cpp
362
+ if (i.inner_it_.index() == 0)
363
+ inner_it_.emplace<0>(std::get<0>(std::move(i.inner_it_)));
364
+ else
365
+ inner_it_.emplace<1>(std::get<1>(std::move(i.inner_it_)));
366
+ ```
367
+
368
+ [*Note 2*: `Const` can only be `true` when *Base* models
369
+ `forward_range`. — *end note*]
370
+
371
+ ``` cpp
372
+ constexpr decltype(auto) operator*() const;
373
+ ```
374
+
375
+ *Effects:* Equivalent to:
376
+
377
+ ``` cpp
378
+ using reference =
379
+ common_reference_t<iter_reference_t<InnerIter>, iter_reference_t<PatternIter>>;
380
+ return visit([](auto& it) -> reference { return *it; }, inner_it_);
381
+ ```
382
+
383
+ ``` cpp
384
+ constexpr iterator& operator++();
385
+ ```
386
+
387
+ *Effects:* Equivalent to:
388
+
389
+ ``` cpp
390
+ visit([](auto& it){ ++it; }, inner_it_);
391
+ satisfy();
392
+ return *this;
393
+ ```
394
+
395
+ ``` cpp
396
+ constexpr void operator++(int);
397
+ ```
398
+
399
+ *Effects:* Equivalent to `++*this`.
400
+
401
+ ``` cpp
402
+ constexpr iterator operator++(int)
403
+ requires ref-is-glvalue && forward_iterator<OuterIter> && forward_iterator<InnerIter>;
404
+ ```
405
+
406
+ *Effects:* Equivalent to:
407
+
408
+ ``` cpp
409
+ iterator tmp = *this;
410
+ ++*this;
411
+ return tmp;
412
+ ```
413
+
414
+ ``` cpp
415
+ constexpr iterator& operator--()
416
+ requires ref-is-glvalue && bidirectional_range<Base> &&
417
+ bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;
418
+ ```
419
+
420
+ *Effects:* Equivalent to:
421
+
422
+ ``` cpp
423
+ if (outer_it_ == ranges::end(parent_->base_)) {
424
+ auto&& inner = *--outer_it_;
425
+ inner_it_.emplace<1>(ranges::end(inner));
426
+ }
427
+
428
+ while (true) {
429
+ if (inner_it_.index() == 0) {
430
+ auto& it = std::get<0>(inner_it_);
431
+ if (it == ranges::begin(parent_->pattern_)) {
432
+ auto&& inner = *--outer_it_;
433
+ inner_it_.emplace<1>(ranges::end(inner));
434
+ } else {
435
+ break;
436
+ }
437
+ } else {
438
+ auto& it = std::get<1>(inner_it_);
439
+ auto&& inner = *outer_it_;
440
+ if (it == ranges::begin(inner)) {
441
+ inner_it_.emplace<0>(ranges::end(parent_->pattern_));
442
+ } else {
443
+ break;
444
+ }
445
+ }
446
+ }
447
+ visit([](auto& it){ --it; }, inner_it_);
448
+ return *this;
449
+ ```
450
+
451
+ ``` cpp
452
+ constexpr iterator operator--(int)
453
+ requires ref-is-glvalue && bidirectional_range<Base> &&
454
+ bidirectional-common<InnerBase> && bidirectional-common<PatternBase>;
455
+ ```
456
+
457
+ *Effects:* Equivalent to:
458
+
459
+ ``` cpp
460
+ iterator tmp = *this;
461
+ --*this;
462
+ return tmp;
463
+ ```
464
+
465
+ ``` cpp
466
+ friend constexpr bool operator==(const iterator& x, const iterator& y)
467
+ requires ref-is-glvalue && forward_range<Base> &&
468
+ equality_comparable<InnerIter>;
469
+ ```
470
+
471
+ *Effects:* Equivalent to:
472
+
473
+ ``` cpp
474
+ return x.outer_it_ == y.outer_it_ && x.inner_it_ == y.inner_it_;
475
+ ```
476
+
477
+ #### Class template `join_with_view::sentinel` <a id="range.join.with.sentinel">[[range.join.with.sentinel]]</a>
478
+
479
+ ``` cpp
480
+ namespace std::ranges {
481
+ template<input_range V, forward_range Pattern>
482
+ requires view<V> && input_range<range_reference_t<V>>
483
+ && view<Pattern> && compatible-joinable-ranges<range_reference_t<V>, Pattern>
484
+ template<bool Const>
485
+ class join_with_view<V, Pattern>::sentinel {
486
+ using Parent = maybe-const<Const, join_with_view>; // exposition only
487
+ using Base = maybe-const<Const, V>; // exposition only
488
+ sentinel_t<Base> end_ = sentinel_t<Base>(); // exposition only
489
+
490
+ constexpr explicit sentinel(Parent& parent); // exposition only
491
+
492
+ public:
493
+ sentinel() = default;
494
+ constexpr sentinel(sentinel<!Const> s)
495
+ requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
496
+
497
+ template<bool OtherConst>
498
+ requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
499
+ friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
500
+ };
501
+ }
502
+ ```
503
+
504
+ ``` cpp
505
+ constexpr explicit sentinel(Parent& parent);
506
+ ```
507
+
508
+ *Effects:* Initializes *end\_* with `ranges::end(parent.`*`base_`*`)`.
509
+
510
+ ``` cpp
511
+ constexpr sentinel(sentinel<!Const> s)
512
+ requires Const && convertible_to<sentinel_t<V>, sentinel_t<Base>>;
513
+ ```
514
+
515
+ *Effects:* Initializes *end\_* with `std::move(s.`*`end_`*`)`.
516
+
517
+ ``` cpp
518
+ template<bool OtherConst>
519
+ requires sentinel_for<sentinel_t<Base>, iterator_t<maybe-const<OtherConst, V>>>
520
+ friend constexpr bool operator==(const iterator<OtherConst>& x, const sentinel& y);
521
+ ```
522
+
523
+ *Effects:* Equivalent to: `return x.`*`outer`*`() == y.`*`end_`*`;`
524
+