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
10
11// std::ranges::rend
12// std::ranges::crend
13
14#include <ranges>
15
16#include <cassert>
17#include <utility>
18#include "test_macros.h"
19#include "test_iterators.h"
20
21using RangeREndT = decltype(std::ranges::rend);
22using RangeCREndT = decltype(std::ranges::crend);
23
24static int globalBuff[8];
25
26static_assert(!std::is_invocable_v<RangeREndT, int (&&)[]>);
27static_assert(!std::is_invocable_v<RangeREndT, int (&)[]>);
28static_assert(!std::is_invocable_v<RangeREndT, int (&&)[10]>);
29static_assert( std::is_invocable_v<RangeREndT, int (&)[10]>);
30static_assert(!std::is_invocable_v<RangeCREndT, int (&&)[]>);
31static_assert(!std::is_invocable_v<RangeCREndT, int (&)[]>);
32static_assert(!std::is_invocable_v<RangeCREndT, int (&&)[10]>);
33static_assert( std::is_invocable_v<RangeCREndT, int (&)[10]>);
34
35struct Incomplete;
36static_assert(!std::is_invocable_v<RangeREndT, Incomplete(&&)[]>);
37static_assert(!std::is_invocable_v<RangeREndT, Incomplete(&&)[42]>);
38static_assert(!std::is_invocable_v<RangeCREndT, Incomplete(&&)[]>);
39static_assert(!std::is_invocable_v<RangeCREndT, Incomplete(&&)[42]>);
40
41struct REndMember {
42 int x;
43 const int* rbegin() const;
44 constexpr const int* rend() const { return &x; }
45};
46
47// Ensure that we can't call with rvalues with borrowing disabled.
48static_assert( std::is_invocable_v<RangeREndT, REndMember&>);
49static_assert(!std::is_invocable_v<RangeREndT, REndMember &&>);
50static_assert( std::is_invocable_v<RangeREndT, REndMember const&>);
51static_assert(!std::is_invocable_v<RangeREndT, REndMember const&&>);
52static_assert( std::is_invocable_v<RangeCREndT, REndMember &>);
53static_assert(!std::is_invocable_v<RangeCREndT, REndMember &&>);
54static_assert( std::is_invocable_v<RangeCREndT, REndMember const&>);
55static_assert(!std::is_invocable_v<RangeCREndT, REndMember const&&>);
56
57constexpr bool testReturnTypes() {
58 {
59 int *x[2];
60 ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator<int**>);
61 ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator<int* const*>);
62 }
63
64 {
65 int x[2][2];
66 ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), std::reverse_iterator<int(*)[2]>);
67 ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), std::reverse_iterator<const int(*)[2]>);
68 }
69
70 {
71 struct Different {
72 char* rbegin();
73 sentinel_wrapper<char*>& rend();
74 short* rbegin() const;
75 sentinel_wrapper<short*>& rend() const;
76 } x;
77 ASSERT_SAME_TYPE(decltype(std::ranges::rend(x)), sentinel_wrapper<char*>);
78 ASSERT_SAME_TYPE(decltype(std::ranges::crend(x)), sentinel_wrapper<short*>);
79 }
80
81 return true;
82}
83
84constexpr bool testArray() {
85 int a[2];
86 assert(std::ranges::rend(a).base() == a);
87 assert(std::ranges::crend(a).base() == a);
88
89 int b[2][2];
90 assert(std::ranges::rend(b).base() == b);
91 assert(std::ranges::crend(b).base() == b);
92
93 REndMember c[2];
94 assert(std::ranges::rend(c).base() == c);
95 assert(std::ranges::crend(c).base() == c);
96
97 return true;
98}
99
100struct REndMemberReturnsInt {
101 int rbegin() const;
102 int rend() const;
103};
104static_assert(!std::is_invocable_v<RangeREndT, REndMemberReturnsInt const&>);
105
106struct REndMemberReturnsVoidPtr {
107 const void *rbegin() const;
108 const void *rend() const;
109};
110static_assert(!std::is_invocable_v<RangeREndT, REndMemberReturnsVoidPtr const&>);
111
112struct PtrConvertible {
113 operator int*() const;
114};
115struct PtrConvertibleREndMember {
116 PtrConvertible rbegin() const;
117 PtrConvertible rend() const;
118};
119static_assert(!std::is_invocable_v<RangeREndT, PtrConvertibleREndMember const&>);
120
121struct NoRBeginMember {
122 constexpr const int* rend();
123};
124static_assert(!std::is_invocable_v<RangeREndT, NoRBeginMember const&>);
125
126struct NonConstREndMember {
127 int x;
128 constexpr int* rbegin() { return nullptr; }
129 constexpr int* rend() { return &x; }
130};
131static_assert( std::is_invocable_v<RangeREndT, NonConstREndMember &>);
132static_assert(!std::is_invocable_v<RangeREndT, NonConstREndMember const&>);
133static_assert(!std::is_invocable_v<RangeCREndT, NonConstREndMember &>);
134static_assert(!std::is_invocable_v<RangeCREndT, NonConstREndMember const&>);
135
136struct EnabledBorrowingREndMember {
137 constexpr int* rbegin() const { return nullptr; }
138 constexpr int* rend() const { return &globalBuff[0]; }
139};
140
141template <>
142inline constexpr bool std::ranges::enable_borrowed_range<EnabledBorrowingREndMember> = true;
143
144struct REndMemberFunction {
145 int x;
146 constexpr const int* rbegin() const { return nullptr; }
147 constexpr const int* rend() const { return &x; }
148 friend constexpr int* rend(REndMemberFunction const&);
149};
150
151struct Empty { };
152struct EmptyEndMember {
153 Empty rbegin() const;
154 Empty rend() const;
155};
156static_assert(!std::is_invocable_v<RangeREndT, EmptyEndMember const&>);
157
158struct EmptyPtrREndMember {
159 Empty x;
160 constexpr const Empty* rbegin() const { return nullptr; }
161 constexpr const Empty* rend() const { return &x; }
162};
163
164constexpr bool testREndMember() {
165 REndMember a;
166 assert(std::ranges::rend(a) == &a.x);
167 assert(std::ranges::crend(a) == &a.x);
168
169 NonConstREndMember b;
170 assert(std::ranges::rend(b) == &b.x);
171 static_assert(!std::is_invocable_v<RangeCREndT, decltype((b))>);
172
173 EnabledBorrowingREndMember c;
174 assert(std::ranges::rend(std::move(c)) == &globalBuff[0]);
175 assert(std::ranges::crend(std::move(c)) == &globalBuff[0]);
176
177 REndMemberFunction d;
178 assert(std::ranges::rend(d) == &d.x);
179 assert(std::ranges::crend(d) == &d.x);
180
181 EmptyPtrREndMember e;
182 assert(std::ranges::rend(e) == &e.x);
183 assert(std::ranges::crend(e) == &e.x);
184
185 return true;
186}
187
188struct REndFunction {
189 int x;
190 friend constexpr const int* rbegin(REndFunction const&) { return nullptr; }
191 friend constexpr const int* rend(REndFunction const& bf) { return &bf.x; }
192};
193
194static_assert( std::is_invocable_v<RangeREndT, REndFunction const&>);
195static_assert(!std::is_invocable_v<RangeREndT, REndFunction &&>);
196
197static_assert( std::is_invocable_v<RangeREndT, REndFunction const&>);
198static_assert(!std::is_invocable_v<RangeREndT, REndFunction &&>);
199static_assert(std::is_invocable_v<RangeREndT, REndFunction&>); // Ill-formed before P2602R2 Poison Pills are Too Toxic
200static_assert( std::is_invocable_v<RangeCREndT, REndFunction const&>);
201static_assert( std::is_invocable_v<RangeCREndT, REndFunction &>);
202
203struct REndFunctionReturnsInt {
204 friend constexpr int rbegin(REndFunctionReturnsInt const&);
205 friend constexpr int rend(REndFunctionReturnsInt const&);
206};
207static_assert(!std::is_invocable_v<RangeREndT, REndFunctionReturnsInt const&>);
208
209struct REndFunctionReturnsVoidPtr {
210 friend constexpr void* rbegin(REndFunctionReturnsVoidPtr const&);
211 friend constexpr void* rend(REndFunctionReturnsVoidPtr const&);
212};
213static_assert(!std::is_invocable_v<RangeREndT, REndFunctionReturnsVoidPtr const&>);
214
215struct REndFunctionReturnsEmpty {
216 friend constexpr Empty rbegin(REndFunctionReturnsEmpty const&);
217 friend constexpr Empty rend(REndFunctionReturnsEmpty const&);
218};
219static_assert(!std::is_invocable_v<RangeREndT, REndFunctionReturnsEmpty const&>);
220
221struct REndFunctionReturnsPtrConvertible {
222 friend constexpr PtrConvertible rbegin(REndFunctionReturnsPtrConvertible const&);
223 friend constexpr PtrConvertible rend(REndFunctionReturnsPtrConvertible const&);
224};
225static_assert(!std::is_invocable_v<RangeREndT, REndFunctionReturnsPtrConvertible const&>);
226
227struct NoRBeginFunction {
228 friend constexpr const int* rend(NoRBeginFunction const&);
229};
230static_assert(!std::is_invocable_v<RangeREndT, NoRBeginFunction const&>);
231
232struct REndFunctionByValue {
233 friend constexpr int* rbegin(REndFunctionByValue) { return nullptr; }
234 friend constexpr int* rend(REndFunctionByValue) { return &globalBuff[1]; }
235};
236static_assert(!std::is_invocable_v<RangeCREndT, REndFunctionByValue>);
237
238struct REndFunctionEnabledBorrowing {
239 friend constexpr int* rbegin(REndFunctionEnabledBorrowing) { return nullptr; }
240 friend constexpr int* rend(REndFunctionEnabledBorrowing) { return &globalBuff[2]; }
241};
242template<>
243inline constexpr bool std::ranges::enable_borrowed_range<REndFunctionEnabledBorrowing> = true;
244
245struct REndFunctionReturnsEmptyPtr {
246 Empty x;
247 friend constexpr const Empty* rbegin(REndFunctionReturnsEmptyPtr const&) { return nullptr; }
248 friend constexpr const Empty* rend(REndFunctionReturnsEmptyPtr const& bf) { return &bf.x; }
249};
250
251struct REndFunctionWithDataMember {
252 int x;
253 int rend;
254 friend constexpr const int* rbegin(REndFunctionWithDataMember const&) { return nullptr; }
255 friend constexpr const int* rend(REndFunctionWithDataMember const& bf) { return &bf.x; }
256};
257
258struct REndFunctionWithPrivateEndMember : private REndMember {
259 int y;
260 friend constexpr const int* rbegin(REndFunctionWithPrivateEndMember const&) { return nullptr; }
261 friend constexpr const int* rend(REndFunctionWithPrivateEndMember const& bf) { return &bf.y; }
262};
263
264struct RBeginMemberEndFunction {
265 int x;
266 constexpr const int* rbegin() const { return nullptr; }
267 friend constexpr const int* rend(RBeginMemberEndFunction const& bf) { return &bf.x; }
268};
269
270constexpr bool testREndFunction() {
271 const REndFunction a{};
272 assert(std::ranges::rend(a) == &a.x);
273 assert(std::ranges::crend(a) == &a.x);
274 REndFunction aa{};
275 assert(std::ranges::rend(aa) == &aa.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
276 assert(std::ranges::crend(aa) == &aa.x);
277
278 REndFunctionByValue b;
279 assert(std::ranges::rend(b) == &globalBuff[1]);
280 assert(std::ranges::crend(b) == &globalBuff[1]);
281
282 REndFunctionEnabledBorrowing c;
283 assert(std::ranges::rend(std::move(c)) == &globalBuff[2]);
284 assert(std::ranges::crend(std::move(c)) == &globalBuff[2]);
285
286 const REndFunctionReturnsEmptyPtr d{};
287 assert(std::ranges::rend(d) == &d.x);
288 assert(std::ranges::crend(d) == &d.x);
289 REndFunctionReturnsEmptyPtr dd{};
290 assert(std::ranges::rend(dd) == &dd.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
291 assert(std::ranges::crend(dd) == &dd.x);
292
293 const REndFunctionWithDataMember e{};
294 assert(std::ranges::rend(e) == &e.x);
295 assert(std::ranges::crend(e) == &e.x);
296 REndFunctionWithDataMember ee{};
297 assert(std::ranges::rend(ee) == &ee.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
298 assert(std::ranges::crend(ee) == &ee.x);
299
300 const REndFunctionWithPrivateEndMember f{};
301 assert(std::ranges::rend(f) == &f.y);
302 assert(std::ranges::crend(f) == &f.y);
303 REndFunctionWithPrivateEndMember ff{};
304 assert(std::ranges::rend(ff) == &ff.y); // Ill-formed before P2602R2 Poison Pills are Too Toxic
305 assert(std::ranges::crend(ff) == &ff.y);
306
307 const RBeginMemberEndFunction g{};
308 assert(std::ranges::rend(g) == &g.x);
309 assert(std::ranges::crend(g) == &g.x);
310 RBeginMemberEndFunction gg{};
311 assert(std::ranges::rend(gg) == &gg.x); // Ill-formed before P2602R2 Poison Pills are Too Toxic
312 assert(std::ranges::crend(gg) == &gg.x);
313
314 return true;
315}
316
317
318struct MemberBeginEnd {
319 int b, e;
320 char cb, ce;
321 constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>(&b); }
322 constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); }
323 constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); }
324 constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); }
325};
326static_assert( std::is_invocable_v<RangeREndT, MemberBeginEnd&>);
327static_assert( std::is_invocable_v<RangeREndT, MemberBeginEnd const&>);
328static_assert( std::is_invocable_v<RangeCREndT, MemberBeginEnd const&>);
329
330struct FunctionBeginEnd {
331 int b, e;
332 char cb, ce;
333 friend constexpr bidirectional_iterator<int*> begin(FunctionBeginEnd& v) {
334 return bidirectional_iterator<int*>(&v.b);
335 }
336 friend constexpr bidirectional_iterator<int*> end(FunctionBeginEnd& v) { return bidirectional_iterator<int*>(&v.e); }
337 friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginEnd& v) {
338 return bidirectional_iterator<const char*>(&v.cb);
339 }
340 friend constexpr bidirectional_iterator<const char*> end(const FunctionBeginEnd& v) {
341 return bidirectional_iterator<const char*>(&v.ce);
342 }
343};
344static_assert( std::is_invocable_v<RangeREndT, FunctionBeginEnd&>);
345static_assert( std::is_invocable_v<RangeREndT, FunctionBeginEnd const&>);
346static_assert( std::is_invocable_v<RangeCREndT, FunctionBeginEnd const&>);
347
348struct MemberBeginFunctionEnd {
349 int b, e;
350 char cb, ce;
351 constexpr bidirectional_iterator<int*> begin() { return bidirectional_iterator<int*>(&b); }
352 friend constexpr bidirectional_iterator<int*> end(MemberBeginFunctionEnd& v) {
353 return bidirectional_iterator<int*>(&v.e);
354 }
355 constexpr bidirectional_iterator<const char*> begin() const { return bidirectional_iterator<const char*>(&cb); }
356 friend constexpr bidirectional_iterator<const char*> end(const MemberBeginFunctionEnd& v) {
357 return bidirectional_iterator<const char*>(&v.ce);
358 }
359};
360static_assert( std::is_invocable_v<RangeREndT, MemberBeginFunctionEnd&>);
361static_assert( std::is_invocable_v<RangeREndT, MemberBeginFunctionEnd const&>);
362static_assert( std::is_invocable_v<RangeCREndT, MemberBeginFunctionEnd const&>);
363
364struct FunctionBeginMemberEnd {
365 int b, e;
366 char cb, ce;
367 friend constexpr bidirectional_iterator<int*> begin(FunctionBeginMemberEnd& v) {
368 return bidirectional_iterator<int*>(&v.b);
369 }
370 constexpr bidirectional_iterator<int*> end() { return bidirectional_iterator<int*>(&e); }
371 friend constexpr bidirectional_iterator<const char*> begin(const FunctionBeginMemberEnd& v) {
372 return bidirectional_iterator<const char*>(&v.cb);
373 }
374 constexpr bidirectional_iterator<const char*> end() const { return bidirectional_iterator<const char*>(&ce); }
375};
376static_assert( std::is_invocable_v<RangeREndT, FunctionBeginMemberEnd&>);
377static_assert( std::is_invocable_v<RangeREndT, FunctionBeginMemberEnd const&>);
378static_assert( std::is_invocable_v<RangeCREndT, FunctionBeginMemberEnd const&>);
379
380struct MemberBeginEndDifferentTypes {
381 bidirectional_iterator<int*> begin();
382 bidirectional_iterator<const int*> end();
383};
384static_assert(!std::is_invocable_v<RangeREndT, MemberBeginEndDifferentTypes&>);
385static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginEndDifferentTypes&>);
386
387struct FunctionBeginEndDifferentTypes {
388 friend bidirectional_iterator<int*> begin(FunctionBeginEndDifferentTypes&);
389 friend bidirectional_iterator<const int*> end(FunctionBeginEndDifferentTypes&);
390};
391static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginEndDifferentTypes&>);
392static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginEndDifferentTypes&>);
393
394struct MemberBeginEndForwardIterators {
395 forward_iterator<int*> begin();
396 forward_iterator<int*> end();
397};
398static_assert(!std::is_invocable_v<RangeREndT, MemberBeginEndForwardIterators&>);
399static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginEndForwardIterators&>);
400
401struct FunctionBeginEndForwardIterators {
402 friend forward_iterator<int*> begin(FunctionBeginEndForwardIterators&);
403 friend forward_iterator<int*> end(FunctionBeginEndForwardIterators&);
404};
405static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginEndForwardIterators&>);
406static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginEndForwardIterators&>);
407
408struct MemberBeginOnly {
409 bidirectional_iterator<int*> begin() const;
410};
411static_assert(!std::is_invocable_v<RangeREndT, MemberBeginOnly&>);
412static_assert(!std::is_invocable_v<RangeCREndT, MemberBeginOnly&>);
413
414struct FunctionBeginOnly {
415 friend bidirectional_iterator<int*> begin(FunctionBeginOnly&);
416};
417static_assert(!std::is_invocable_v<RangeREndT, FunctionBeginOnly&>);
418static_assert(!std::is_invocable_v<RangeCREndT, FunctionBeginOnly&>);
419
420struct MemberEndOnly {
421 bidirectional_iterator<int*> end() const;
422};
423static_assert(!std::is_invocable_v<RangeREndT, MemberEndOnly&>);
424static_assert(!std::is_invocable_v<RangeCREndT, MemberEndOnly&>);
425
426struct FunctionEndOnly {
427 friend bidirectional_iterator<int*> end(FunctionEndOnly&);
428};
429static_assert(!std::is_invocable_v<RangeREndT, FunctionEndOnly&>);
430static_assert(!std::is_invocable_v<RangeCREndT, FunctionEndOnly&>);
431
432// Make sure there is no clash between the following cases:
433// - the case that handles classes defining member `rbegin` and `rend` functions;
434// - the case that handles classes defining `begin` and `end` functions returning reversible iterators.
435struct MemberBeginAndRBegin {
436 int* begin() const;
437 int* end() const;
438 int* rbegin() const;
439 int* rend() const;
440};
441static_assert( std::is_invocable_v<RangeREndT, MemberBeginAndRBegin&>);
442static_assert( std::is_invocable_v<RangeCREndT, MemberBeginAndRBegin&>);
443static_assert( std::same_as<std::invoke_result_t<RangeREndT, MemberBeginAndRBegin&>, int*>);
444static_assert( std::same_as<std::invoke_result_t<RangeCREndT, MemberBeginAndRBegin&>, int*>);
445
446constexpr bool testBeginEnd() {
447 MemberBeginEnd a{};
448 const MemberBeginEnd aa{};
449 assert(base(std::ranges::rend(a).base()) == &a.b);
450 assert(base(std::ranges::crend(a).base()) == &a.cb);
451 assert(base(std::ranges::rend(aa).base()) == &aa.cb);
452 assert(base(std::ranges::crend(aa).base()) == &aa.cb);
453
454 FunctionBeginEnd b{};
455 const FunctionBeginEnd bb{};
456 assert(base(std::ranges::rend(b).base()) == &b.b);
457 assert(base(std::ranges::crend(b).base()) == &b.cb);
458 assert(base(std::ranges::rend(bb).base()) == &bb.cb);
459 assert(base(std::ranges::crend(bb).base()) == &bb.cb);
460
461 MemberBeginFunctionEnd c{};
462 const MemberBeginFunctionEnd cc{};
463 assert(base(std::ranges::rend(c).base()) == &c.b);
464 assert(base(std::ranges::crend(c).base()) == &c.cb);
465 assert(base(std::ranges::rend(cc).base()) == &cc.cb);
466 assert(base(std::ranges::crend(cc).base()) == &cc.cb);
467
468 FunctionBeginMemberEnd d{};
469 const FunctionBeginMemberEnd dd{};
470 assert(base(std::ranges::rend(d).base()) == &d.b);
471 assert(base(std::ranges::crend(d).base()) == &d.cb);
472 assert(base(std::ranges::rend(dd).base()) == &dd.cb);
473 assert(base(std::ranges::crend(dd).base()) == &dd.cb);
474
475 return true;
476}
477
478
479ASSERT_NOEXCEPT(std::ranges::rend(std::declval<int (&)[10]>()));
480ASSERT_NOEXCEPT(std::ranges::crend(std::declval<int (&)[10]>()));
481
482struct NoThrowMemberREnd {
483 ThrowingIterator<int> rbegin() const;
484 ThrowingIterator<int> rend() const noexcept; // auto(t.rend()) doesn't throw
485} ntmre;
486static_assert(noexcept(std::ranges::rend(ntmre)));
487static_assert(noexcept(std::ranges::crend(ntmre)));
488
489struct NoThrowADLREnd {
490 ThrowingIterator<int> rbegin() const;
491 friend ThrowingIterator<int> rend(NoThrowADLREnd&) noexcept; // auto(rend(t)) doesn't throw
492 friend ThrowingIterator<int> rend(const NoThrowADLREnd&) noexcept;
493} ntare;
494static_assert(noexcept(std::ranges::rend(ntare)));
495static_assert(noexcept(std::ranges::crend(ntare)));
496
497struct NoThrowMemberREndReturnsRef {
498 ThrowingIterator<int> rbegin() const;
499 ThrowingIterator<int>& rend() const noexcept; // auto(t.rend()) may throw
500} ntmrerr;
501static_assert(!noexcept(std::ranges::rend(ntmrerr)));
502static_assert(!noexcept(std::ranges::crend(ntmrerr)));
503
504struct REndReturnsArrayRef {
505 auto rbegin() const noexcept -> int(&)[10];
506 auto rend() const noexcept -> int(&)[10];
507} rerar;
508static_assert(noexcept(std::ranges::rend(rerar)));
509static_assert(noexcept(std::ranges::crend(rerar)));
510
511struct NoThrowBeginThrowingEnd {
512 int* begin() const noexcept;
513 int* end() const;
514} ntbte;
515static_assert(noexcept(std::ranges::rend(ntbte)));
516static_assert(noexcept(std::ranges::crend(ntbte)));
517
518struct NoThrowEndThrowingBegin {
519 int* begin() const;
520 int* end() const noexcept;
521} ntetb;
522static_assert(!noexcept(std::ranges::rend(ntetb)));
523static_assert(!noexcept(std::ranges::crend(ntetb)));
524
525// Test ADL-proofing.
526struct Incomplete;
527template<class T> struct Holder { T t; };
528static_assert(!std::is_invocable_v<RangeREndT, Holder<Incomplete>*>);
529static_assert(!std::is_invocable_v<RangeREndT, Holder<Incomplete>*&>);
530static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*>);
531static_assert(!std::is_invocable_v<RangeCREndT, Holder<Incomplete>*&>);
532
533int main(int, char**) {
534 static_assert(testReturnTypes());
535
536 testArray();
537 static_assert(testArray());
538
539 testREndMember();
540 static_assert(testREndMember());
541
542 testREndFunction();
543 static_assert(testREndFunction());
544
545 testBeginEnd();
546 static_assert(testBeginEnd());
547
548 return 0;
549}
550

source code of libcxx/test/std/ranges/range.access/rend.pass.cpp