Skip to content

Commit 5fc844a

Browse files
H-G-HristovZingam
andauthored
[libc++] P2944R3: Constrained comparisons - optional (#144249)
Partially implements [P2944R3](https://wg21.link/P2944R3) which adds constrained comparisons to std::optional. Closes #136767 # References [optional.relops](https://wg21.link/optional.relops) [optional.comp.with.t](https://wg21.link/optional.comp.with.t) --------- Co-authored-by: Hristo Hristov <[email protected]>
1 parent de551c6 commit 5fc844a

File tree

15 files changed

+266
-45
lines changed

15 files changed

+266
-45
lines changed

libcxx/docs/Status/Cxx2cPapers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -59,7 +59,7 @@
5959
"`P2248R8 <https://wg21.link/P2248R8>`__","Enabling list-initialization for algorithms","2024-03 (Tokyo)","","",""
6060
"`P2810R4 <https://wg21.link/P2810R4>`__","``is_debugger_present`` ``is_replaceable``","2024-03 (Tokyo)","","",""
6161
"`P1068R11 <https://wg21.link/P1068R11>`__","Vector API for random number generation","2024-03 (Tokyo)","","",""
62-
"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","","The changes to ``optional`` and ``tuple``'s equality overload from P2165R4 are not yet implemented"
62+
"`P2944R3 <https://wg21.link/P2944R3>`__","Comparisons for ``reference_wrapper``","2024-03 (Tokyo)","|Partial|","","The changes to ``tuple``'s equality overload from P2165R4 are not yet implemented."
6363
"`P2642R6 <https://wg21.link/P2642R6>`__","Padded ``mdspan`` layouts","2024-03 (Tokyo)","","",""
6464
"`P3029R1 <https://wg21.link/P3029R1>`__","Better ``mdspan``'s CTAD","2024-03 (Tokyo)","|Complete|","19",""
6565
"","","","","",""

libcxx/include/__type_traits/is_core_convertible.h

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -24,20 +24,23 @@ _LIBCPP_BEGIN_NAMESPACE_STD
2424
// and __is_core_convertible<immovable-type,immovable-type> is true in C++17 and later.
2525

2626
template <class _Tp, class _Up, class = void>
27-
struct __is_core_convertible : false_type {};
27+
inline const bool __is_core_convertible_v = false;
2828

2929
template <class _Tp, class _Up>
30-
struct __is_core_convertible<_Tp, _Up, decltype(static_cast<void (*)(_Up)>(0)(static_cast<_Tp (*)()>(0)()))>
31-
: true_type {};
30+
inline const bool
31+
__is_core_convertible_v<_Tp, _Up, decltype(static_cast<void (*)(_Up)>(0)(static_cast<_Tp (*)()>(0)()))> = true;
32+
33+
template <class _Tp, class _Up>
34+
using __is_core_convertible _LIBCPP_NODEBUG = integral_constant<bool, __is_core_convertible_v<_Tp, _Up> >;
3235

3336
#if _LIBCPP_STD_VER >= 20
3437

3538
template <class _Tp, class _Up>
36-
concept __core_convertible_to = __is_core_convertible<_Tp, _Up>::value;
39+
concept __core_convertible_to = __is_core_convertible_v<_Tp, _Up>;
3740

3841
#endif // _LIBCPP_STD_VER >= 20
3942

40-
template <class _Tp, class _Up, bool = __is_core_convertible<_Tp, _Up>::value>
43+
template <class _Tp, class _Up, bool = __is_core_convertible_v<_Tp, _Up> >
4144
inline const bool __is_nothrow_core_convertible_v = false;
4245

4346
#ifndef _LIBCPP_CXX03_LANG

libcxx/include/optional

Lines changed: 55 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -205,6 +205,7 @@ namespace std {
205205
# include <__type_traits/is_assignable.h>
206206
# include <__type_traits/is_constructible.h>
207207
# include <__type_traits/is_convertible.h>
208+
# include <__type_traits/is_core_convertible.h>
208209
# include <__type_traits/is_destructible.h>
209210
# include <__type_traits/is_nothrow_assignable.h>
210211
# include <__type_traits/is_nothrow_constructible.h>
@@ -982,11 +983,13 @@ public:
982983
template <class _Tp>
983984
optional(_Tp) -> optional<_Tp>;
984985

985-
// Comparisons between optionals
986+
// [optional.relops] Relational operators
987+
986988
template <
987989
class _Tp,
988990
class _Up,
989-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>, int> = 0>
991+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
992+
int> = 0>
990993
_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const optional<_Up>& __y) {
991994
if (static_cast<bool>(__x) != static_cast<bool>(__y))
992995
return false;
@@ -998,7 +1001,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const
9981001
template <
9991002
class _Tp,
10001003
class _Up,
1001-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>, int> = 0>
1004+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
1005+
int> = 0>
10021006
_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const optional<_Up>& __y) {
10031007
if (static_cast<bool>(__x) != static_cast<bool>(__y))
10041008
return true;
@@ -1007,10 +1011,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const
10071011
return *__x != *__y;
10081012
}
10091013

1010-
template <
1011-
class _Tp,
1012-
class _Up,
1013-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>, int> = 0>
1014+
template < class _Tp,
1015+
class _Up,
1016+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
1017+
int> = 0>
10141018
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const optional<_Up>& __y) {
10151019
if (!static_cast<bool>(__y))
10161020
return false;
@@ -1019,10 +1023,10 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const o
10191023
return *__x < *__y;
10201024
}
10211025

1022-
template <
1023-
class _Tp,
1024-
class _Up,
1025-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>, int> = 0>
1026+
template < class _Tp,
1027+
class _Up,
1028+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
1029+
int> = 0>
10261030
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const optional<_Up>& __y) {
10271031
if (!static_cast<bool>(__x))
10281032
return false;
@@ -1034,7 +1038,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const o
10341038
template <
10351039
class _Tp,
10361040
class _Up,
1037-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>, int> = 0>
1041+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
1042+
int> = 0>
10381043
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const optional<_Up>& __y) {
10391044
if (!static_cast<bool>(__x))
10401045
return true;
@@ -1046,7 +1051,8 @@ _LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const
10461051
template <
10471052
class _Tp,
10481053
class _Up,
1049-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>, int> = 0>
1054+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
1055+
int> = 0>
10501056
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const optional<_Up>& __y) {
10511057
if (!static_cast<bool>(__y))
10521058
return true;
@@ -1067,7 +1073,8 @@ operator<=>(const optional<_Tp>& __x, const optional<_Up>& __y) {
10671073

10681074
# endif // _LIBCPP_STD_VER >= 20
10691075

1070-
// Comparisons with nullopt
1076+
// [optional.nullops] Comparison with nullopt
1077+
10711078
template <class _Tp>
10721079
_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, nullopt_t) noexcept {
10731080
return !static_cast<bool>(__x);
@@ -1139,99 +1146,108 @@ _LIBCPP_HIDE_FROM_ABI constexpr strong_ordering operator<=>(const optional<_Tp>&
11391146

11401147
# endif // _LIBCPP_STD_VER <= 17
11411148

1142-
// Comparisons with T
1149+
// [optional.comp.with.t] Comparison with T
1150+
11431151
template <
11441152
class _Tp,
11451153
class _Up,
1146-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>, int> = 0>
1154+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
1155+
int> = 0>
11471156
_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const optional<_Tp>& __x, const _Up& __v) {
11481157
return static_cast<bool>(__x) ? *__x == __v : false;
11491158
}
11501159

11511160
template <
11521161
class _Tp,
11531162
class _Up,
1154-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>, int> = 0>
1163+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() == std::declval<const _Up&>()), bool>,
1164+
int> = 0>
11551165
_LIBCPP_HIDE_FROM_ABI constexpr bool operator==(const _Tp& __v, const optional<_Up>& __x) {
11561166
return static_cast<bool>(__x) ? __v == *__x : false;
11571167
}
11581168

11591169
template <
11601170
class _Tp,
11611171
class _Up,
1162-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>, int> = 0>
1172+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
1173+
int> = 0>
11631174
_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const optional<_Tp>& __x, const _Up& __v) {
11641175
return static_cast<bool>(__x) ? *__x != __v : true;
11651176
}
11661177

11671178
template <
11681179
class _Tp,
11691180
class _Up,
1170-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>, int> = 0>
1181+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() != std::declval<const _Up&>()), bool>,
1182+
int> = 0>
11711183
_LIBCPP_HIDE_FROM_ABI constexpr bool operator!=(const _Tp& __v, const optional<_Up>& __x) {
11721184
return static_cast<bool>(__x) ? __v != *__x : true;
11731185
}
11741186

1175-
template <
1176-
class _Tp,
1177-
class _Up,
1178-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>, int> = 0>
1187+
template < class _Tp,
1188+
class _Up,
1189+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
1190+
int> = 0>
11791191
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const optional<_Tp>& __x, const _Up& __v) {
11801192
return static_cast<bool>(__x) ? *__x < __v : true;
11811193
}
11821194

1183-
template <
1184-
class _Tp,
1185-
class _Up,
1186-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>, int> = 0>
1195+
template < class _Tp,
1196+
class _Up,
1197+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() < std::declval<const _Up&>()), bool>,
1198+
int> = 0>
11871199
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<(const _Tp& __v, const optional<_Up>& __x) {
11881200
return static_cast<bool>(__x) ? __v < *__x : false;
11891201
}
11901202

11911203
template <
11921204
class _Tp,
11931205
class _Up,
1194-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>, int> = 0>
1206+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
1207+
int> = 0>
11951208
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const optional<_Tp>& __x, const _Up& __v) {
11961209
return static_cast<bool>(__x) ? *__x <= __v : true;
11971210
}
11981211

11991212
template <
12001213
class _Tp,
12011214
class _Up,
1202-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>, int> = 0>
1215+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() <= std::declval<const _Up&>()), bool>,
1216+
int> = 0>
12031217
_LIBCPP_HIDE_FROM_ABI constexpr bool operator<=(const _Tp& __v, const optional<_Up>& __x) {
12041218
return static_cast<bool>(__x) ? __v <= *__x : false;
12051219
}
12061220

1207-
template <
1208-
class _Tp,
1209-
class _Up,
1210-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>, int> = 0>
1221+
template < class _Tp,
1222+
class _Up,
1223+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
1224+
int> = 0>
12111225
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const optional<_Tp>& __x, const _Up& __v) {
12121226
return static_cast<bool>(__x) ? *__x > __v : false;
12131227
}
12141228

1215-
template <
1216-
class _Tp,
1217-
class _Up,
1218-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>, int> = 0>
1229+
template < class _Tp,
1230+
class _Up,
1231+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() > std::declval<const _Up&>()), bool>,
1232+
int> = 0>
12191233
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>(const _Tp& __v, const optional<_Up>& __x) {
12201234
return static_cast<bool>(__x) ? __v > *__x : true;
12211235
}
12221236

12231237
template <
12241238
class _Tp,
12251239
class _Up,
1226-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>, int> = 0>
1240+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
1241+
int> = 0>
12271242
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const optional<_Tp>& __x, const _Up& __v) {
12281243
return static_cast<bool>(__x) ? *__x >= __v : false;
12291244
}
12301245

12311246
template <
12321247
class _Tp,
12331248
class _Up,
1234-
enable_if_t<is_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>, int> = 0>
1249+
enable_if_t<__is_core_convertible_v<decltype(std::declval<const _Tp&>() >= std::declval<const _Up&>()), bool>,
1250+
int> = 0>
12351251
_LIBCPP_HIDE_FROM_ABI constexpr bool operator>=(const _Tp& __v, const optional<_Up>& __x) {
12361252
return static_cast<bool>(__x) ? __v >= *__x : true;
12371253
}

libcxx/test/std/utilities/optional/optional.comp_with_t/equal.pass.cpp

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,29 @@
1414

1515
#include <optional>
1616

17+
#include "test_comparisons.h"
1718
#include "test_macros.h"
1819

20+
#if TEST_STD_VER >= 26
21+
22+
// Test SFINAE.
23+
24+
static_assert(HasOperatorEqual<int, std::optional<int>>);
25+
static_assert(HasOperatorEqual<int, std::optional<EqualityComparable>>);
26+
static_assert(HasOperatorEqual<EqualityComparable, std::optional<EqualityComparable>>);
27+
28+
static_assert(!HasOperatorEqual<NonComparable, std::optional<NonComparable>>);
29+
static_assert(!HasOperatorEqual<NonComparable, std::optional<EqualityComparable>>);
30+
31+
static_assert(HasOperatorEqual<std::optional<int>, int>);
32+
static_assert(HasOperatorEqual<std::optional<EqualityComparable>, int>);
33+
static_assert(HasOperatorEqual<std::optional<EqualityComparable>, EqualityComparable>);
34+
35+
static_assert(!HasOperatorEqual<std::optional<NonComparable>, NonComparable>);
36+
static_assert(!HasOperatorEqual<std::optional<EqualityComparable>, NonComparable>);
37+
38+
#endif
39+
1940
using std::optional;
2041

2142
struct X {

libcxx/test/std/utilities/optional/optional.comp_with_t/greater.pass.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,28 @@
1414

1515
#include <optional>
1616

17+
#include "test_comparisons.h"
1718
#include "test_macros.h"
1819

20+
#if TEST_STD_VER >= 26
21+
22+
// Test SFINAE.
23+
static_assert(HasOperatorGreaterThan<std::optional<ThreeWayComparable>, int>);
24+
static_assert(HasOperatorGreaterThan<std::optional<ThreeWayComparable>, ThreeWayComparable>);
25+
26+
static_assert(!HasOperatorGreaterThan<std::optional<NonComparable>, NonComparable>);
27+
static_assert(!HasOperatorGreaterThan<std::optional<ThreeWayComparable>, NonComparable>);
28+
static_assert(!HasOperatorGreaterThan<std::optional<NonComparable>, ThreeWayComparable>);
29+
30+
static_assert(HasOperatorGreaterThan<int, std::optional<ThreeWayComparable>>);
31+
static_assert(HasOperatorGreaterThan<ThreeWayComparable, std::optional<ThreeWayComparable>>);
32+
33+
static_assert(!HasOperatorGreaterThan<NonComparable, std::optional<NonComparable>>);
34+
static_assert(!HasOperatorGreaterThan<NonComparable, std::optional<ThreeWayComparable>>);
35+
static_assert(!HasOperatorGreaterThan<ThreeWayComparable, std::optional<NonComparable>>);
36+
37+
#endif
38+
1939
using std::optional;
2040

2141
struct X {

libcxx/test/std/utilities/optional/optional.comp_with_t/greater_equal.pass.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,28 @@
1414

1515
#include <optional>
1616

17+
#include "test_comparisons.h"
1718
#include "test_macros.h"
1819

20+
#if TEST_STD_VER >= 26
21+
22+
// Test SFINAE.
23+
static_assert(HasOperatorGreaterThanEqual<std::optional<ThreeWayComparable>, int>);
24+
static_assert(HasOperatorGreaterThanEqual<std::optional<ThreeWayComparable>, ThreeWayComparable>);
25+
26+
static_assert(!HasOperatorGreaterThanEqual<std::optional<NonComparable>, NonComparable>);
27+
static_assert(!HasOperatorGreaterThanEqual<std::optional<ThreeWayComparable>, NonComparable>);
28+
static_assert(!HasOperatorGreaterThanEqual<std::optional<NonComparable>, ThreeWayComparable>);
29+
30+
static_assert(HasOperatorGreaterThanEqual<int, std::optional<ThreeWayComparable>>);
31+
static_assert(HasOperatorGreaterThanEqual<ThreeWayComparable, std::optional<ThreeWayComparable>>);
32+
33+
static_assert(!HasOperatorGreaterThanEqual<NonComparable, std::optional<NonComparable>>);
34+
static_assert(!HasOperatorGreaterThanEqual<NonComparable, std::optional<ThreeWayComparable>>);
35+
static_assert(!HasOperatorGreaterThanEqual<ThreeWayComparable, std::optional<NonComparable>>);
36+
37+
#endif
38+
1939
using std::optional;
2040

2141
struct X {

libcxx/test/std/utilities/optional/optional.comp_with_t/less_equal.pass.cpp

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,8 +14,28 @@
1414

1515
#include <optional>
1616

17+
#include "test_comparisons.h"
1718
#include "test_macros.h"
1819

20+
#if TEST_STD_VER >= 26
21+
22+
// Test SFINAE.
23+
static_assert(HasOperatorLessThanEqual<std::optional<ThreeWayComparable>, int>);
24+
static_assert(HasOperatorLessThanEqual<std::optional<ThreeWayComparable>, ThreeWayComparable>);
25+
26+
static_assert(!HasOperatorLessThanEqual<std::optional<NonComparable>, NonComparable>);
27+
static_assert(!HasOperatorLessThanEqual<std::optional<ThreeWayComparable>, NonComparable>);
28+
static_assert(!HasOperatorLessThanEqual<std::optional<NonComparable>, ThreeWayComparable>);
29+
30+
static_assert(HasOperatorLessThanEqual<int, std::optional<ThreeWayComparable>>);
31+
static_assert(HasOperatorLessThanEqual<ThreeWayComparable, std::optional<ThreeWayComparable>>);
32+
33+
static_assert(!HasOperatorLessThanEqual<NonComparable, std::optional<NonComparable>>);
34+
static_assert(!HasOperatorLessThanEqual<NonComparable, std::optional<ThreeWayComparable>>);
35+
static_assert(!HasOperatorLessThanEqual<ThreeWayComparable, std::optional<NonComparable>>);
36+
37+
#endif
38+
1939
using std::optional;
2040

2141
struct X {

0 commit comments

Comments
 (0)