]> git.ipfire.org Git - thirdparty/gcc.git/blob - libstdc++-v3/libsupc++/compare
libstdc++: Implement spaceship for std::pair (P1614R2)
[thirdparty/gcc.git] / libstdc++-v3 / libsupc++ / compare
1 // -*- C++ -*- operator<=> three-way comparison support.
2
3 // Copyright (C) 2019 Free Software Foundation, Inc.
4 //
5 // This file is part of GCC.
6 //
7 // GCC is free software; you can redistribute it and/or modify
8 // it under the terms of the GNU General Public License as published by
9 // the Free Software Foundation; either version 3, or (at your option)
10 // any later version.
11 //
12 // GCC is distributed in the hope that it will be useful,
13 // but WITHOUT ANY WARRANTY; without even the implied warranty of
14 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 // GNU General Public License for more details.
16 //
17 // Under Section 7 of GPL version 3, you are granted additional
18 // permissions described in the GCC Runtime Library Exception, version
19 // 3.1, as published by the Free Software Foundation.
20
21 // You should have received a copy of the GNU General Public License and
22 // a copy of the GCC Runtime Library Exception along with this program;
23 // see the files COPYING3 and COPYING.RUNTIME respectively. If not, see
24 // <http://www.gnu.org/licenses/>.
25
26 /** @file compare
27 * This is a Standard C++ Library header.
28 */
29
30 #ifndef _COMPARE
31 #define _COMPARE
32
33 #pragma GCC system_header
34
35 #if __cplusplus > 201703L && __cpp_impl_three_way_comparison >= 201907L
36
37 #pragma GCC visibility push(default)
38
39 #include <concepts>
40
41 namespace std
42 {
43 #define __cpp_lib_three_way_comparison 201711L
44
45 // [cmp.categories], comparison category types
46
47 namespace __cmp_cat
48 {
49 enum class _Eq
50 { equal = 0, equivalent = equal, nonequal = 1, nonequivalent = nonequal };
51
52 enum class _Ord { _Less = -1, _Greater = 1 };
53
54 enum class _Ncmp { _Unordered = -127 };
55
56 struct __unspec
57 {
58 constexpr __unspec(__unspec*) { }
59 };
60 }
61
62 class partial_ordering
63 {
64 int _M_value;
65 bool _M_is_ordered;
66
67 constexpr explicit
68 partial_ordering(__cmp_cat::_Eq __v) noexcept
69 : _M_value(int(__v)), _M_is_ordered(true)
70 { }
71
72 constexpr explicit
73 partial_ordering(__cmp_cat::_Ord __v) noexcept
74 : _M_value(int(__v)), _M_is_ordered(true)
75 { }
76
77 constexpr explicit
78 partial_ordering(__cmp_cat::_Ncmp __v) noexcept
79 : _M_value(int(__v)), _M_is_ordered(false)
80 { }
81
82 public:
83 // valid values
84 static const partial_ordering less;
85 static const partial_ordering equivalent;
86 static const partial_ordering greater;
87 static const partial_ordering unordered;
88
89 // comparisons
90 friend constexpr bool
91 operator==(partial_ordering __v, __cmp_cat::__unspec) noexcept
92 { return __v._M_is_ordered && __v._M_value == 0; }
93
94 friend constexpr bool
95 operator==(partial_ordering, partial_ordering) noexcept = default;
96
97 friend constexpr bool
98 operator< (partial_ordering __v, __cmp_cat::__unspec) noexcept
99 { return __v._M_is_ordered && __v._M_value < 0; }
100
101 friend constexpr bool
102 operator> (partial_ordering __v, __cmp_cat::__unspec) noexcept
103 { return __v._M_is_ordered && __v._M_value > 0; }
104
105 friend constexpr bool
106 operator<=(partial_ordering __v, __cmp_cat::__unspec) noexcept
107 { return __v._M_is_ordered && __v._M_value <= 0; }
108
109 friend constexpr bool
110 operator>=(partial_ordering __v, __cmp_cat::__unspec) noexcept
111 { return __v._M_is_ordered && __v._M_value >= 0; }
112
113 friend constexpr bool
114 operator< (__cmp_cat::__unspec, partial_ordering __v) noexcept
115 { return __v._M_is_ordered && 0 < __v._M_value; }
116
117 friend constexpr bool
118 operator> (__cmp_cat::__unspec, partial_ordering __v) noexcept
119 { return __v._M_is_ordered && 0 > __v._M_value; }
120
121 friend constexpr bool
122 operator<=(__cmp_cat::__unspec, partial_ordering __v) noexcept
123 { return __v._M_is_ordered && 0 <= __v._M_value; }
124
125 friend constexpr bool
126 operator>=(__cmp_cat::__unspec, partial_ordering __v) noexcept
127 { return __v._M_is_ordered && 0 >= __v._M_value; }
128
129 friend constexpr partial_ordering
130 operator<=>(partial_ordering __v, __cmp_cat::__unspec) noexcept
131 { return __v; }
132
133 friend constexpr partial_ordering
134 operator<=>(__cmp_cat::__unspec, partial_ordering __v) noexcept
135 {
136 if (__v < 0)
137 return partial_ordering::greater;
138 else if (__v > 0)
139 return partial_ordering::less;
140 else
141 return __v;
142 }
143 };
144
145 // valid values' definitions
146 inline constexpr partial_ordering
147 partial_ordering::less(__cmp_cat::_Ord::_Less);
148
149 inline constexpr partial_ordering
150 partial_ordering::equivalent(__cmp_cat::_Eq::equivalent);
151
152 inline constexpr partial_ordering
153 partial_ordering::greater(__cmp_cat::_Ord::_Greater);
154
155 inline constexpr partial_ordering
156 partial_ordering::unordered(__cmp_cat::_Ncmp::_Unordered);
157
158 class weak_ordering
159 {
160 int _M_value;
161
162 constexpr explicit
163 weak_ordering(__cmp_cat::_Eq __v) noexcept : _M_value(int(__v))
164 { }
165
166 constexpr explicit
167 weak_ordering(__cmp_cat::_Ord __v) noexcept : _M_value(int(__v))
168 { }
169
170 public:
171 // valid values
172 static const weak_ordering less;
173 static const weak_ordering equivalent;
174 static const weak_ordering greater;
175
176 constexpr operator partial_ordering() const noexcept
177 {
178 if (_M_value == 0)
179 return partial_ordering::equivalent;
180 else if (_M_value < 0)
181 return partial_ordering::less;
182 else
183 return partial_ordering::greater;
184 }
185
186 // comparisons
187 friend constexpr bool
188 operator==(weak_ordering __v, __cmp_cat::__unspec) noexcept
189 { return __v._M_value == 0; }
190
191 friend constexpr bool
192 operator==(weak_ordering, weak_ordering) noexcept = default;
193
194 friend constexpr bool
195 operator< (weak_ordering __v, __cmp_cat::__unspec) noexcept
196 { return __v._M_value < 0; }
197
198 friend constexpr bool
199 operator> (weak_ordering __v, __cmp_cat::__unspec) noexcept
200 { return __v._M_value > 0; }
201
202 friend constexpr bool
203 operator<=(weak_ordering __v, __cmp_cat::__unspec) noexcept
204 { return __v._M_value <= 0; }
205
206 friend constexpr bool
207 operator>=(weak_ordering __v, __cmp_cat::__unspec) noexcept
208 { return __v._M_value >= 0; }
209
210 friend constexpr bool
211 operator< (__cmp_cat::__unspec, weak_ordering __v) noexcept
212 { return 0 < __v._M_value; }
213
214 friend constexpr bool
215 operator> (__cmp_cat::__unspec, weak_ordering __v) noexcept
216 { return 0 > __v._M_value; }
217
218 friend constexpr bool
219 operator<=(__cmp_cat::__unspec, weak_ordering __v) noexcept
220 { return 0 <= __v._M_value; }
221
222 friend constexpr bool
223 operator>=(__cmp_cat::__unspec, weak_ordering __v) noexcept
224 { return 0 >= __v._M_value; }
225
226 friend constexpr weak_ordering
227 operator<=>(weak_ordering __v, __cmp_cat::__unspec) noexcept
228 { return __v; }
229
230 friend constexpr weak_ordering
231 operator<=>(__cmp_cat::__unspec, weak_ordering __v) noexcept
232 {
233 if (__v < 0)
234 return weak_ordering::greater;
235 else if (__v > 0)
236 return weak_ordering::less;
237 else
238 return __v;
239 }
240 };
241
242 // valid values' definitions
243 inline constexpr weak_ordering
244 weak_ordering::less(__cmp_cat::_Ord::_Less);
245
246 inline constexpr weak_ordering
247 weak_ordering::equivalent(__cmp_cat::_Eq::equivalent);
248
249 inline constexpr weak_ordering
250 weak_ordering::greater(__cmp_cat::_Ord::_Greater);
251
252 class strong_ordering
253 {
254 int _M_value;
255
256 constexpr explicit
257 strong_ordering(__cmp_cat::_Eq __v) noexcept
258 : _M_value(int(__v))
259 { }
260
261 constexpr explicit
262 strong_ordering(__cmp_cat::_Ord __v) noexcept
263 : _M_value(int(__v))
264 { }
265
266 public:
267 // valid values
268 static const strong_ordering less;
269 static const strong_ordering equal;
270 static const strong_ordering equivalent;
271 static const strong_ordering greater;
272
273 constexpr operator partial_ordering() const noexcept
274 {
275 if (_M_value == 0)
276 return partial_ordering::equivalent;
277 else if (_M_value < 0)
278 return partial_ordering::less;
279 else
280 return partial_ordering::greater;
281 }
282
283 constexpr operator weak_ordering() const noexcept
284 {
285 if (_M_value == 0)
286 return weak_ordering::equivalent;
287 else if (_M_value < 0)
288 return weak_ordering::less;
289 else
290 return weak_ordering::greater;
291 }
292
293 // comparisons
294 friend constexpr bool
295 operator==(strong_ordering __v, __cmp_cat::__unspec) noexcept
296 { return __v._M_value == 0; }
297
298 friend constexpr bool
299 operator==(strong_ordering, strong_ordering) noexcept = default;
300
301 friend constexpr bool
302 operator< (strong_ordering __v, __cmp_cat::__unspec) noexcept
303 { return __v._M_value < 0; }
304
305 friend constexpr bool
306 operator> (strong_ordering __v, __cmp_cat::__unspec) noexcept
307 { return __v._M_value > 0; }
308
309 friend constexpr bool
310 operator<=(strong_ordering __v, __cmp_cat::__unspec) noexcept
311 { return __v._M_value <= 0; }
312
313 friend constexpr bool
314 operator>=(strong_ordering __v, __cmp_cat::__unspec) noexcept
315 { return __v._M_value >= 0; }
316
317 friend constexpr bool
318 operator< (__cmp_cat::__unspec, strong_ordering __v) noexcept
319 { return 0 < __v._M_value; }
320
321 friend constexpr bool
322 operator> (__cmp_cat::__unspec, strong_ordering __v) noexcept
323 { return 0 > __v._M_value; }
324
325 friend constexpr bool
326 operator<=(__cmp_cat::__unspec, strong_ordering __v) noexcept
327 { return 0 <= __v._M_value; }
328
329 friend constexpr bool
330 operator>=(__cmp_cat::__unspec, strong_ordering __v) noexcept
331 { return 0 >= __v._M_value; }
332
333 friend constexpr strong_ordering
334 operator<=>(strong_ordering __v, __cmp_cat::__unspec) noexcept
335 { return __v; }
336
337 friend constexpr strong_ordering
338 operator<=>(__cmp_cat::__unspec, strong_ordering __v) noexcept
339 {
340 if (__v < 0)
341 return strong_ordering::greater;
342 else if (__v > 0)
343 return strong_ordering::less;
344 else
345 return __v;
346 }
347 };
348
349 // valid values' definitions
350 inline constexpr strong_ordering
351 strong_ordering::less(__cmp_cat::_Ord::_Less);
352
353 inline constexpr strong_ordering
354 strong_ordering::equal(__cmp_cat::_Eq::equal);
355
356 inline constexpr strong_ordering
357 strong_ordering::equivalent(__cmp_cat::_Eq::equivalent);
358
359 inline constexpr strong_ordering
360 strong_ordering::greater(__cmp_cat::_Ord::_Greater);
361
362
363 // named comparison functions
364 constexpr bool
365 is_eq(partial_ordering __cmp) noexcept
366 { return __cmp == 0; }
367
368 constexpr bool
369 is_neq(partial_ordering __cmp) noexcept
370 { return __cmp != 0; }
371
372 constexpr bool
373 is_lt (partial_ordering __cmp) noexcept
374 { return __cmp < 0; }
375
376 constexpr bool
377 is_lteq(partial_ordering __cmp) noexcept
378 { return __cmp <= 0; }
379
380 constexpr bool
381 is_gt (partial_ordering __cmp) noexcept
382 { return __cmp > 0; }
383
384 constexpr bool
385 is_gteq(partial_ordering __cmp) noexcept
386 { return __cmp >= 0; }
387
388 #if __cpp_lib_concepts
389 namespace __detail
390 {
391 template<typename _Tp>
392 inline constexpr unsigned __cmp_cat_id = 1;
393 template<>
394 inline constexpr unsigned __cmp_cat_id<strong_ordering> = 2;
395 template<>
396 inline constexpr unsigned __cmp_cat_id<weak_ordering> = 4;
397 template<>
398 inline constexpr unsigned __cmp_cat_id<partial_ordering> = 8;
399
400 template<typename... _Ts>
401 constexpr unsigned __cmp_cat_ids()
402 { return (__cmp_cat_id<_Ts> | ...); }
403
404 template<unsigned>
405 struct __common_cmp_cat;
406
407 // If any Ti is not a comparison category type, U is void.
408 template<unsigned _Bits>
409 requires ((_Bits & 1) == 1)
410 struct __common_cmp_cat<_Bits> { using type = void; };
411
412 // Otherwise, if at least one Ti is std::partial_ordering,
413 // U is std::partial_ordering.
414 template<unsigned _Bits>
415 requires ((_Bits & 0b1001) == 0b1000)
416 struct __common_cmp_cat<_Bits> { using type = partial_ordering; };
417
418 // Otherwise, if at least one Ti is std::weak_ordering,
419 // U is std::weak_ordering.
420 template<unsigned _Bits>
421 requires ((_Bits & 0b1101) == 0b0100)
422 struct __common_cmp_cat<_Bits> { using type = weak_ordering; };
423
424 // Otherwise, U is std::strong_ordering.
425 template<>
426 struct __common_cmp_cat<0b0010> { using type = strong_ordering; };
427 } // namespace __detail
428
429 // [cmp.common], common comparison category type
430 template<typename... _Ts>
431 struct common_comparison_category
432 {
433 using type
434 = __detail::__common_cmp_cat<__detail::__cmp_cat_ids<_Ts...>()>::type;
435 };
436
437 // Partial specializations for one and zero argument cases.
438
439 template<typename _Tp>
440 struct common_comparison_category<_Tp>
441 { using type = void; };
442
443 template<>
444 struct common_comparison_category<partial_ordering>
445 { using type = partial_ordering; };
446
447 template<>
448 struct common_comparison_category<weak_ordering>
449 { using type = weak_ordering; };
450
451 template<>
452 struct common_comparison_category<strong_ordering>
453 { using type = strong_ordering; };
454
455 template<>
456 struct common_comparison_category<>
457 { using type = strong_ordering; };
458
459 template<typename... _Ts>
460 using common_comparison_category_t
461 = typename common_comparison_category<_Ts...>::type;
462
463 namespace __detail
464 {
465 template<typename _Tp, typename _Cat>
466 concept __compares_as
467 = same_as<common_comparison_category_t<_Tp, _Cat>, _Cat>;
468
469 template<typename _Tp, typename _Up>
470 concept __partially_ordered_with
471 = requires(const remove_reference_t<_Tp>& __t,
472 const remove_reference_t<_Up>& __u) {
473 { __t < __u } -> boolean;
474 { __t > __u } -> boolean;
475 { __t <= __u } -> boolean;
476 { __t >= __u } -> boolean;
477 { __u < __t } -> boolean;
478 { __u > __t } -> boolean;
479 { __u <= __t } -> boolean;
480 { __u >= __t } -> boolean;
481 };
482 } // namespace __detail
483
484 // [cmp.concept], concept three_way_comparable
485 template<typename _Tp, typename _Cat = partial_ordering>
486 concept three_way_comparable
487 = __detail::__weakly_eq_cmp_with<_Tp, _Tp>
488 && (!convertible_to<_Cat, partial_ordering>
489 || __detail::__partially_ordered_with<_Tp, _Tp>)
490 && requires(const remove_reference_t<_Tp>& __a,
491 const remove_reference_t<_Tp>& __b) {
492 { __a <=> __b } -> __detail::__compares_as<_Cat>;
493 };
494
495 template<typename _Tp, typename _Up, typename _Cat = partial_ordering>
496 concept three_way_comparable_with
497 = __detail::__weakly_eq_cmp_with<_Tp, _Up>
498 && (!convertible_to<_Cat, partial_ordering>
499 || __detail::__partially_ordered_with<_Tp, _Up>)
500 && three_way_comparable<_Tp, _Cat>
501 && three_way_comparable<_Up, _Cat>
502 && common_reference_with<const remove_reference_t<_Tp>&,
503 const remove_reference_t<_Up>&>
504 && three_way_comparable<
505 common_reference_t<const remove_reference_t<_Tp>&,
506 const remove_reference_t<_Up>&>, _Cat>
507 && requires(const remove_reference_t<_Tp>& __t,
508 const remove_reference_t<_Up>& __u) {
509 { __t <=> __u } -> __detail::__compares_as<_Cat>;
510 { __u <=> __t } -> __detail::__compares_as<_Cat>;
511 };
512
513 namespace __detail
514 {
515 template<typename _Tp, typename _Up>
516 using __cmp3way_res_t
517 = decltype(std::declval<_Tp>() <=> std::declval<_Up>());
518
519 // Implementation of std::compare_three_way_result.
520 // It is undefined for a program to add specializations of
521 // std::compare_three_way_result, so the std::compare_three_way_result_t
522 // alias ignores std::compare_three_way_result and uses
523 // __detail::__cmp3way_res_impl directly instead.
524 template<typename _Tp, typename _Up>
525 struct __cmp3way_res_impl
526 { };
527
528 template<typename _Tp, typename _Up>
529 requires requires { typename __cmp3way_res_t<__cref<_Tp>, __cref<_Up>>; }
530 struct __cmp3way_res_impl<_Tp, _Up>
531 {
532 using type = __cmp3way_res_t<__cref<_Tp>, __cref<_Up>>;
533 };
534 } // namespace __detail
535
536 /// [cmp.result], result of three-way comparison
537 template<typename _Tp, typename _Up = _Tp>
538 struct compare_three_way_result
539 : __detail::__cmp3way_res_impl<_Tp, _Up>
540 { };
541
542 /// [cmp.result], result of three-way comparison
543 template<typename _Tp, typename _Up = _Tp>
544 using compare_three_way_result_t
545 = typename __detail::__cmp3way_res_impl<_Tp, _Up>::type;
546
547 namespace __detail
548 {
549 // BUILTIN-PTR-THREE-WAY(T, U)
550 template<typename _Tp, typename _Up>
551 concept __3way_builtin_ptr_cmp
552 = convertible_to<_Tp, const volatile void*>
553 && convertible_to<_Up, const volatile void*>
554 && ! requires(_Tp&& __t, _Up&& __u)
555 { operator<=>(static_cast<_Tp&&>(__t), static_cast<_Up&&>(__u)); }
556 && ! requires(_Tp&& __t, _Up&& __u)
557 { static_cast<_Tp&&>(__t).operator<=>(static_cast<_Up&&>(__u)); };
558
559 // FIXME: workaround for PR c++/91073
560 template<typename _Tp, typename _Up>
561 concept __3way_cmp_with = three_way_comparable_with<_Tp, _Up>;
562 } // namespace __detail
563
564 // [cmp.object], typename compare_three_way
565 struct compare_three_way
566 {
567 template<typename _Tp, typename _Up>
568 requires (__detail::__3way_cmp_with<_Tp, _Up>
569 || __detail::__3way_builtin_ptr_cmp<_Tp, _Up>)
570 constexpr auto
571 operator()(_Tp&& __t, _Up&& __u) const noexcept
572 {
573 if constexpr (__detail::__3way_builtin_ptr_cmp<_Tp, _Up>)
574 {
575 auto __pt = static_cast<const volatile void*>(__t);
576 auto __pu = static_cast<const volatile void*>(__u);
577 if (__builtin_is_constant_evaluated())
578 return __pt <=> __pu;
579 auto __it = reinterpret_cast<__UINTPTR_TYPE__>(__pt);
580 auto __iu = reinterpret_cast<__UINTPTR_TYPE__>(__pu);
581 return __it <=> __iu;
582 }
583 else
584 return static_cast<_Tp&&>(__t) <=> static_cast<_Up&&>(__u);
585 }
586
587 using is_transparent = void;
588 };
589
590 namespace __cmp_cust
591 {
592 template<floating_point _Tp>
593 constexpr weak_ordering
594 __fp_weak_ordering(_Tp __e, _Tp __f)
595 {
596 // Returns an integer with the same sign as the argument, and magnitude
597 // indicating the classification: zero=1 subnorm=2 norm=3 inf=4 nan=5
598 auto __cat = [](_Tp __fp) -> int {
599 const int __sign = __builtin_signbit(__fp) ? -1 : 1;
600 if (__builtin_isnormal(__fp))
601 return (__fp == 0 ? 1 : 3) * __sign;
602 if (__builtin_isnan(__fp))
603 return 5 * __sign;
604 if (int __inf = __builtin_isinf_sign(__fp))
605 return 4 * __inf;
606 return 2 * __sign;
607 };
608
609 auto __po = __e <=> __f;
610 if (is_lt(__po))
611 return weak_ordering::less;
612 else if (is_gt(__po))
613 return weak_ordering::greater;
614 else if (__po == partial_ordering::equivalent)
615 return weak_ordering::equivalent;
616 else // unordered, at least one argument is NaN
617 {
618 // return -1 for negative nan, +1 for positive nan, 0 otherwise.
619 auto __isnan_sign = [](_Tp __fp) -> int {
620 return __builtin_isnan(__fp)
621 ? __builtin_signbit(__fp) ? -1 : 1
622 : 0;
623 };
624 auto __ord = __isnan_sign(__e) <=> __isnan_sign(__f);
625 if (is_eq(__ord))
626 return weak_ordering::equivalent;
627 else if (is_lt(__ord))
628 return weak_ordering::less;
629 else
630 return weak_ordering::greater;
631 }
632 }
633
634 template<typename _Tp, typename _Up>
635 concept __adl_strong = requires(_Tp&& __t, _Up&& __u)
636 {
637 strong_ordering(strong_order(static_cast<_Tp&&>(__t),
638 static_cast<_Up&&>(__u)));
639 };
640
641 template<typename _Tp, typename _Up>
642 concept __adl_weak = requires(_Tp&& __t, _Up&& __u)
643 {
644 weak_ordering(weak_order(static_cast<_Tp&&>(__t),
645 static_cast<_Up&&>(__u)));
646 };
647
648 template<typename _Tp, typename _Up>
649 concept __adl_partial = requires(_Tp&& __t, _Up&& __u)
650 {
651 partial_ordering(partial_order(static_cast<_Tp&&>(__t),
652 static_cast<_Up&&>(__u)));
653 };
654
655 template<typename _Ord, typename _Tp, typename _Up>
656 concept __op_cmp = requires(_Tp&& __t, _Up&& __u)
657 {
658 _Ord(static_cast<_Tp&&>(__t) <=> static_cast<_Up&&>(__u));
659 };
660
661 template<typename _Tp, typename _Up>
662 concept __strongly_ordered
663 = __adl_strong<_Tp, _Up>
664 // FIXME: || floating_point<remove_reference_t<_Tp>>
665 || __op_cmp<strong_ordering, _Tp, _Up>;
666
667 class _Strong_order
668 {
669 template<typename _Tp, typename _Up>
670 static constexpr bool
671 _S_noexcept()
672 {
673 if constexpr (floating_point<decay_t<_Tp>>)
674 return true;
675 else if constexpr (__adl_strong<_Tp, _Up>)
676 return noexcept(strong_ordering(strong_order(std::declval<_Tp>(),
677 std::declval<_Up>())));
678 else if constexpr (__op_cmp<strong_ordering, _Tp, _Up>)
679 return noexcept(std::declval<_Tp>() <=> std::declval<_Up>());
680 }
681
682 friend class _Weak_order;
683 friend class _Strong_fallback;
684
685 public:
686 template<typename _Tp, typename _Up>
687 requires __strongly_ordered<_Tp, _Up>
688 constexpr strong_ordering
689 operator()(_Tp&& __e, _Up&& __f) const
690 noexcept(_S_noexcept<_Tp, _Up>())
691 {
692 static_assert(same_as<decay_t<_Tp>, decay_t<_Up>>);
693
694 /* FIXME:
695 if constexpr (floating_point<decay_t<_Tp>>)
696 return __cmp_cust::__fp_strong_order(__e, __f);
697 else */ if constexpr (__adl_strong<_Tp, _Up>)
698 return strong_ordering(strong_order(static_cast<_Tp&&>(__e),
699 static_cast<_Up&&>(__f)));
700 else if constexpr (__op_cmp<strong_ordering, _Tp, _Up>)
701 return static_cast<_Tp&&>(__e) <=> static_cast<_Up&&>(__f);
702 }
703 };
704
705 template<typename _Tp, typename _Up>
706 concept __weakly_ordered
707 = floating_point<remove_reference_t<_Tp>>
708 || __adl_weak<_Tp, _Up>
709 || __op_cmp<weak_ordering, _Tp, _Up>
710 || __strongly_ordered<_Tp, _Up>;
711
712 class _Weak_order
713 {
714 template<typename _Tp, typename _Up>
715 static constexpr bool
716 _S_noexcept()
717 {
718 if constexpr (floating_point<decay_t<_Tp>>)
719 return true;
720 else if constexpr (__adl_weak<_Tp, _Up>)
721 return noexcept(weak_ordering(weak_order(std::declval<_Tp>(),
722 std::declval<_Up>())));
723 else if constexpr (__op_cmp<weak_ordering, _Tp, _Up>)
724 return noexcept(std::declval<_Tp>() <=> std::declval<_Up>());
725 else if constexpr (__strongly_ordered<_Tp, _Up>)
726 return _Strong_order::_S_noexcept<_Tp, _Up>();
727 }
728
729 friend class _Partial_order;
730 friend class _Weak_fallback;
731
732 public:
733 template<typename _Tp, typename _Up>
734 requires __weakly_ordered<_Tp, _Up>
735 constexpr weak_ordering
736 operator()(_Tp&& __e, _Up&& __f) const
737 noexcept(_S_noexcept<_Tp, _Up>())
738 {
739 static_assert(same_as<decay_t<_Tp>, decay_t<_Up>>);
740
741 if constexpr (floating_point<decay_t<_Tp>>)
742 return __cmp_cust::__fp_weak_ordering(__e, __f);
743 else if constexpr (__adl_weak<_Tp, _Up>)
744 return weak_ordering(weak_order(static_cast<_Tp&&>(__e),
745 static_cast<_Up&&>(__f)));
746 else if constexpr (__op_cmp<weak_ordering, _Tp, _Up>)
747 return static_cast<_Tp&&>(__e) <=> static_cast<_Up&&>(__f);
748 else if constexpr (__strongly_ordered<_Tp, _Up>)
749 return _Strong_order{}(static_cast<_Tp&&>(__e),
750 static_cast<_Up&&>(__f));
751 }
752 };
753
754 template<typename _Tp, typename _Up>
755 concept __partially_ordered
756 = __adl_partial<_Tp, _Up>
757 || __op_cmp<partial_ordering, _Tp, _Up>
758 || __weakly_ordered<_Tp, _Up>;
759
760 class _Partial_order
761 {
762 template<typename _Tp, typename _Up>
763 static constexpr bool
764 _S_noexcept()
765 {
766 if constexpr (__adl_partial<_Tp, _Up>)
767 return noexcept(partial_ordering(partial_order(std::declval<_Tp>(),
768 std::declval<_Up>())));
769 else if constexpr (__op_cmp<partial_ordering, _Tp, _Up>)
770 return noexcept(std::declval<_Tp>() <=> std::declval<_Up>());
771 else if constexpr (__weakly_ordered<_Tp, _Up>)
772 return _Weak_order::_S_noexcept<_Tp, _Up>();
773 }
774
775 friend class _Partial_fallback;
776
777 public:
778 template<typename _Tp, typename _Up>
779 requires __partially_ordered<_Tp, _Up>
780 constexpr partial_ordering
781 operator()(_Tp&& __e, _Up&& __f) const
782 noexcept(_S_noexcept<_Tp, _Up>())
783 {
784 static_assert(same_as<decay_t<_Tp>, decay_t<_Up>>);
785
786 if constexpr (__adl_partial<_Tp, _Up>)
787 return partial_ordering(partial_order(static_cast<_Tp&&>(__e),
788 static_cast<_Up&&>(__f)));
789 else if constexpr (__op_cmp<partial_ordering, _Tp, _Up>)
790 return static_cast<_Tp&&>(__e) <=> static_cast<_Up&&>(__f);
791 else if constexpr (__weakly_ordered<_Tp, _Up>)
792 return _Weak_order{}(static_cast<_Tp&&>(__e),
793 static_cast<_Up&&>(__f));
794 }
795 };
796
797 template<typename _Tp, typename _Up>
798 concept __op_eq_lt = requires(_Tp&& __t, _Up&& __u)
799 {
800 { static_cast<_Tp&&>(__t) == static_cast<_Up&&>(__u) }
801 -> convertible_to<bool>;
802 { static_cast<_Tp&&>(__t) < static_cast<_Up&&>(__u) }
803 -> convertible_to<bool>;
804 };
805
806 class _Strong_fallback
807 {
808 template<typename _Tp, typename _Up>
809 static constexpr bool
810 _S_noexcept()
811 {
812 if constexpr (__strongly_ordered<_Tp, _Up>)
813 return _Strong_order::_S_noexcept<_Tp, _Up>();
814 else
815 return noexcept(bool(std::declval<_Tp>() == std::declval<_Up>()))
816 && noexcept(bool(std::declval<_Tp>() < std::declval<_Up>()));
817 }
818
819 public:
820 template<typename _Tp, typename _Up>
821 requires __strongly_ordered<_Tp, _Up> || __op_eq_lt<_Tp, _Up>
822 constexpr decltype(auto)
823 operator()(_Tp&& __e, _Up&& __f) const
824 noexcept(_S_noexcept<_Tp, _Up>())
825 {
826 static_assert(same_as<decay_t<_Tp>, decay_t<_Up>>);
827
828 if constexpr (__strongly_ordered<_Tp, _Up>)
829 return _Strong_order{}(static_cast<_Tp&&>(__e),
830 static_cast<_Up&&>(__f));
831 else if constexpr (__op_eq_lt<_Tp, _Up>)
832 return static_cast<_Tp&&>(__e) == static_cast<_Up&&>(__f)
833 ? strong_ordering::equal
834 : static_cast<_Tp&&>(__e) < static_cast<_Up&&>(__f)
835 ? strong_ordering::less
836 : strong_ordering::greater;
837 }
838 };
839
840 class _Weak_fallback
841 {
842 template<typename _Tp, typename _Up>
843 static constexpr bool
844 _S_noexcept()
845 {
846 if constexpr (__weakly_ordered<_Tp, _Up>)
847 return _Weak_order::_S_noexcept<_Tp, _Up>();
848 else
849 return noexcept(bool(std::declval<_Tp>() == std::declval<_Up>()))
850 && noexcept(bool(std::declval<_Tp>() < std::declval<_Up>()));
851 }
852
853 public:
854 template<typename _Tp, typename _Up>
855 requires __weakly_ordered<_Tp, _Up> || __op_eq_lt<_Tp, _Up>
856 constexpr decltype(auto)
857 operator()(_Tp&& __e, _Up&& __f) const
858 noexcept(_S_noexcept<_Tp, _Up>())
859 {
860 static_assert(same_as<decay_t<_Tp>, decay_t<_Up>>);
861
862 if constexpr (__weakly_ordered<_Tp, _Up>)
863 return _Weak_order{}(static_cast<_Tp&&>(__e),
864 static_cast<_Up&&>(__f));
865 else if constexpr (__op_eq_lt<_Tp, _Up>)
866 return static_cast<_Tp&&>(__e) == static_cast<_Up&&>(__f)
867 ? weak_ordering::equivalent
868 : static_cast<_Tp&&>(__e) < static_cast<_Up&&>(__f)
869 ? weak_ordering::less
870 : weak_ordering::greater;
871 }
872 };
873
874 class _Partial_fallback
875 {
876 template<typename _Tp, typename _Up>
877 static constexpr bool
878 _S_noexcept()
879 {
880 if constexpr (__partially_ordered<_Tp, _Up>)
881 return _Partial_order::_S_noexcept<_Tp, _Up>();
882 else
883 return noexcept(bool(std::declval<_Tp>() == std::declval<_Up>()))
884 && noexcept(bool(std::declval<_Tp>() < std::declval<_Up>()));
885 }
886
887 public:
888 template<typename _Tp, typename _Up>
889 requires __partially_ordered<_Tp, _Up> || __op_eq_lt<_Tp, _Up>
890 constexpr decltype(auto)
891 operator()(_Tp&& __e, _Up&& __f) const
892 noexcept(_S_noexcept<_Tp, _Up>())
893 {
894 static_assert(same_as<decay_t<_Tp>, decay_t<_Up>>);
895
896 if constexpr (__partially_ordered<_Tp, _Up>)
897 return _Partial_order{}(static_cast<_Tp&&>(__e),
898 static_cast<_Up&&>(__f));
899 else if constexpr (__op_eq_lt<_Tp, _Up>)
900 return static_cast<_Tp&&>(__e) == static_cast<_Up&&>(__f)
901 ? partial_ordering::equivalent
902 : static_cast<_Tp&&>(__e) < static_cast<_Up&&>(__f)
903 ? partial_ordering::less
904 : static_cast<_Up&&>(__f) < static_cast<_Tp&&>(__e)
905 ? partial_ordering::greater
906 : partial_ordering::unordered;
907 }
908 };
909 } // namespace __cmp_cust
910
911 // [cmp.alg], comparison algorithms
912 inline namespace __cmp_alg
913 {
914 inline constexpr __cmp_cust::_Strong_order strong_order{};
915
916 inline constexpr __cmp_cust::_Weak_order weak_order{};
917
918 inline constexpr __cmp_cust::_Partial_order partial_order{};
919
920 inline constexpr __cmp_cust::_Strong_fallback
921 compare_strong_order_fallback{};
922
923 inline constexpr __cmp_cust::_Weak_fallback
924 compare_weak_order_fallback{};
925
926 inline constexpr __cmp_cust::_Partial_fallback
927 compare_partial_order_fallback{};
928 }
929
930 namespace __detail
931 {
932 // [expos.only.func]
933 inline constexpr struct _Synth3way
934 {
935 template<typename _Tp, typename _Up>
936 constexpr auto
937 operator()(const _Tp& __t, const _Up& __u) const
938 requires requires
939 {
940 { __t < __u } -> convertible_to<bool>;
941 { __u < __t } -> convertible_to<bool>;
942 }
943 {
944 if constexpr (__3way_cmp_with<_Tp, _Up>)
945 return __t <=> __u;
946 else
947 {
948 if (__t < __u)
949 return weak_ordering::less;
950 else if (__u < __t)
951 return weak_ordering::greater;
952 else
953 return weak_ordering::equivalent;
954 }
955 }
956 } __synth3way = {};
957
958 template<typename _Tp, typename _Up = _Tp>
959 using __synth3way_t
960 = decltype(__detail::__synth3way(std::declval<_Tp&>(),
961 std::declval<_Up&>()));
962 } // namespace __detail
963 #endif // concepts
964 } // namespace std
965
966 #pragma GCC visibility pop
967
968 #endif // C++20
969
970 #endif // _COMPARE