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