Skip to content

Commit 9d4fbca

Browse files
[libc++] Implement comparison operators for tuple added in C++23
And constrain the new `operator==` since C++26. This patch implements parts of P2165R4, P2944R3, and a possibly improved resolution of LWG3882. Currently, libstdc++ and MSVC STL constrain the new overloads in the same way.
1 parent de551c6 commit 9d4fbca

File tree

8 files changed

+821
-354
lines changed

8 files changed

+821
-354
lines changed

libcxx/docs/Status/Cxx23Papers.csv

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@
6060
"`P1642R11 <https://wg21.link/P1642R11>`__","Freestanding ``[utilities]``, ``[ranges]``, and ``[iterators]``","2022-07 (Virtual)","","",""
6161
"`P1899R3 <https://wg21.link/P1899R3>`__","``stride_view``","2022-07 (Virtual)","","",""
6262
"`P2093R14 <https://wg21.link/P2093R14>`__","Formatted output","2022-07 (Virtual)","|Complete|","18",""
63-
"`P2165R4 <https://wg21.link/P2165R4>`__","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","2022-07 (Virtual)","|Partial|","","Only the part for ``zip_view`` is implemented."
63+
"`P2165R4 <https://wg21.link/P2165R4>`__","Compatibility between ``tuple``, ``pair`` and ``tuple-like`` objects","2022-07 (Virtual)","|Partial|","","Changes of ``tuple``, ``adjacent_view``, and ``cartesian_product_view`` are not yet implemented."
6464
"`P2278R4 <https://wg21.link/P2278R4>`__","``cbegin`` should always return a constant iterator","2022-07 (Virtual)","","",""
6565
"`P2286R8 <https://wg21.link/P2286R8>`__","Formatting Ranges","2022-07 (Virtual)","|Complete|","16",""
6666
"`P2291R3 <https://wg21.link/P2291R3>`__","Add Constexpr Modifiers to Functions ``to_chars`` and ``from_chars`` for Integral Types in ``<charconv>`` Header","2022-07 (Virtual)","|Complete|","16",""

libcxx/docs/Status/Cxx2cIssues.csv

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -149,4 +149,5 @@
149149
"`LWG3343 <https://wg21.link/LWG3343>`__","Ordering of calls to ``unlock()`` and ``notify_all()`` in Effects element of ``notify_all_at_thread_exit()`` should be reversed","Not Adopted Yet","|Complete|","16",""
150150
"`LWG4139 <https://wg21.link/LWG4139>`__","§[time.zone.leap] recursive constraint in <=>","Not Adopted Yet","|Complete|","20",""
151151
"`LWG3456 <https://wg21.link/LWG3456>`__","Pattern used by std::from_chars is underspecified (option B)","Not Adopted Yet","|Complete|","20",""
152+
"`LWG3882 <https://wg21.link/LWG3882>`__","``tuple`` relational operators have confused friendships","Not Adopted Yet","|Complete|","21","The comparsion operators are constrained harder than the proposed resolution. libstdc++ and MSVC STL do the same."
152153
"","","","","",""

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 ``optional`` 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/tuple

Lines changed: 103 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -230,6 +230,7 @@ template <class... Types>
230230
# include <__tuple/sfinae_helpers.h>
231231
# include <__tuple/tuple_element.h>
232232
# include <__tuple/tuple_indices.h>
233+
# include <__tuple/tuple_like.h>
233234
# include <__tuple/tuple_like_ext.h>
234235
# include <__tuple/tuple_size.h>
235236
# include <__tuple/tuple_types.h>
@@ -288,6 +289,72 @@ _LIBCPP_BEGIN_NAMESPACE_STD
288289

289290
# ifndef _LIBCPP_CXX03_LANG
290291

292+
template <size_t _Ip>
293+
struct __tuple_equal {
294+
template <class _Tp, class _Up>
295+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool operator()(const _Tp& __x, const _Up& __y) {
296+
return __tuple_equal<_Ip - 1>()(__x, __y) && std::get<_Ip - 1>(__x) == std::get<_Ip - 1>(__y);
297+
}
298+
299+
# if _LIBCPP_STD_VER >= 26
300+
template <class _Tp, class _Up>
301+
static constexpr bool __can_compare =
302+
__tuple_equal<_Ip - 1>::template __can_compare<_Tp, _Up> && requires(const _Tp& __x, const _Up& __y) {
303+
{ std::get<_Ip - 1>(__x) == std::get<_Ip - 1>(__y) } -> __boolean_testable;
304+
};
305+
# endif // _LIBCPP_STD_VER >= 26
306+
};
307+
308+
template <>
309+
struct __tuple_equal<0> {
310+
template <class _Tp, class _Up>
311+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool operator()(const _Tp&, const _Up&) {
312+
return true;
313+
}
314+
315+
# if _LIBCPP_STD_VER >= 26
316+
template <class _Tp, class _Up>
317+
static constexpr bool __can_compare = true;
318+
# endif // _LIBCPP_STD_VER >= 26
319+
};
320+
321+
# if _LIBCPP_STD_VER >= 20
322+
template <class _Ret, class _Tp, class _Up, size_t... _Is>
323+
_LIBCPP_HIDE_FROM_ABI constexpr _Ret __tuple_compare_three_way(const _Tp& __x, const _Up& __y, index_sequence<_Is...>) {
324+
_Ret __result = strong_ordering::equal;
325+
static_cast<void>(
326+
((__result = std::__synth_three_way(std::get<_Is>(__x), std::get<_Is>(__y)), __result != 0) || ...));
327+
return __result;
328+
}
329+
# endif // _LIBCPP_STD_VER >= 20
330+
331+
# if _LIBCPP_STD_VER >= 23
332+
template <class>
333+
inline constexpr bool __is_tuple_v = false;
334+
335+
template <class... _Tp>
336+
inline constexpr bool __is_tuple_v<tuple<_Tp...>> = true;
337+
338+
template <class _Tp>
339+
concept __tuple_like_no_tuple = __tuple_like<_Tp> && !__is_tuple_v<_Tp>;
340+
341+
template <class _Tp, class _Up, class _IndexSeq>
342+
struct __tuple_common_comparison_category_impl {};
343+
template <class _Tp, class _Up, size_t... _Indices>
344+
requires requires {
345+
typename common_comparison_category_t<
346+
__synth_three_way_result<tuple_element_t<_Indices, _Tp>, tuple_element_t<_Indices, _Up>>...>;
347+
}
348+
struct __tuple_common_comparison_category_impl<_Tp, _Up, index_sequence<_Indices...>> {
349+
using type _LIBCPP_NODEBUG = common_comparison_category_t<
350+
__synth_three_way_result<tuple_element_t<_Indices, _Tp>, tuple_element_t<_Indices, _Up>>...>;
351+
};
352+
353+
template <__tuple_like _Tp, __tuple_like _Up>
354+
using __tuple_common_comparison_category _LIBCPP_NODEBUG =
355+
__tuple_common_comparison_category_impl<_Tp, _Up, make_index_sequence<tuple_size_v<_Tp>>>::type;
356+
# endif // _LIBCPP_STD_VER >= 23
357+
291358
// __tuple_leaf
292359

293360
template <size_t _Ip, class _Hp, bool = is_empty<_Hp>::value && !__libcpp_is_final<_Hp>::value >
@@ -997,7 +1064,25 @@ public:
9971064
noexcept(__all<is_nothrow_swappable_v<const _Tp&>...>::value) {
9981065
__base_.swap(__t.__base_);
9991066
}
1000-
# endif // _LIBCPP_STD_VER >= 23
1067+
1068+
template <__tuple_like_no_tuple _UTuple>
1069+
# if _LIBCPP_STD_VER >= 26
1070+
requires(__tuple_equal<sizeof...(_Tp)>::template __can_compare<tuple, _UTuple>) &&
1071+
(sizeof...(_Tp) == tuple_size_v<_UTuple>)
1072+
# endif // _LIBCPP_STD_VER >= 26
1073+
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const tuple& __x, const _UTuple& __y) {
1074+
static_assert(sizeof...(_Tp) == tuple_size_v<_UTuple>, "Can't compare tuple-like values of different sizes");
1075+
return __tuple_equal<sizeof...(_Tp)>()(__x, __y);
1076+
}
1077+
1078+
template <__tuple_like_no_tuple _UTuple>
1079+
requires(sizeof...(_Tp) == tuple_size_v<_UTuple>)
1080+
_LIBCPP_HIDE_FROM_ABI friend constexpr __tuple_common_comparison_category<tuple, _UTuple>
1081+
operator<=>(const tuple& __x, const _UTuple& __y) {
1082+
return std::__tuple_compare_three_way<__tuple_common_comparison_category<tuple, _UTuple>>(
1083+
__x, __y, index_sequence_for<_Tp...>{});
1084+
}
1085+
# endif // _LIBCPP_STD_VER >= 23
10011086
};
10021087

10031088
_LIBCPP_DIAGNOSTIC_PUSH
@@ -1019,6 +1104,21 @@ public:
10191104
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX20 void swap(tuple&) _NOEXCEPT {}
10201105
# if _LIBCPP_STD_VER >= 23
10211106
_LIBCPP_HIDE_FROM_ABI constexpr void swap(const tuple&) const noexcept {}
1107+
1108+
template <__tuple_like_no_tuple _UTuple>
1109+
# if _LIBCPP_STD_VER >= 26
1110+
requires(tuple_size_v<_UTuple> == 0)
1111+
# endif // _LIBCPP_STD_VER >= 26
1112+
_LIBCPP_HIDE_FROM_ABI friend constexpr bool operator==(const tuple& __x, const _UTuple& __y) {
1113+
static_assert(tuple_size_v<_UTuple> == 0, "Can't compare tuple-like values of different sizes");
1114+
return true;
1115+
}
1116+
1117+
template <__tuple_like_no_tuple _UTuple>
1118+
requires(tuple_size_v<_UTuple> == 0)
1119+
_LIBCPP_HIDE_FROM_ABI friend constexpr strong_ordering operator<=>(const tuple& __x, const _UTuple& __y) {
1120+
return strong_ordering::equal;
1121+
}
10221122
# endif
10231123
};
10241124
_LIBCPP_DIAGNOSTIC_POP
@@ -1137,22 +1237,6 @@ inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 tuple<_Tp&&...> forwa
11371237
return tuple<_Tp&&...>(std::forward<_Tp>(__t)...);
11381238
}
11391239

1140-
template <size_t _Ip>
1141-
struct __tuple_equal {
1142-
template <class _Tp, class _Up>
1143-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool operator()(const _Tp& __x, const _Up& __y) {
1144-
return __tuple_equal<_Ip - 1>()(__x, __y) && std::get<_Ip - 1>(__x) == std::get<_Ip - 1>(__y);
1145-
}
1146-
};
1147-
1148-
template <>
1149-
struct __tuple_equal<0> {
1150-
template <class _Tp, class _Up>
1151-
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 bool operator()(const _Tp&, const _Up&) {
1152-
return true;
1153-
}
1154-
};
1155-
11561240
template <class... _Tp, class... _Up>
11571241
# if _LIBCPP_STD_VER >= 26
11581242
requires(__all<requires(const _Tp& __t, const _Up& __u) {
@@ -1169,20 +1253,12 @@ operator==(const tuple<_Tp...>& __x, const tuple<_Up...>& __y) {
11691253

11701254
// operator<=>
11711255

1172-
template <class... _Tp, class... _Up, size_t... _Is>
1173-
_LIBCPP_HIDE_FROM_ABI constexpr auto
1174-
__tuple_compare_three_way(const tuple<_Tp...>& __x, const tuple<_Up...>& __y, index_sequence<_Is...>) {
1175-
common_comparison_category_t<__synth_three_way_result<_Tp, _Up>...> __result = strong_ordering::equal;
1176-
static_cast<void>(
1177-
((__result = std::__synth_three_way(std::get<_Is>(__x), std::get<_Is>(__y)), __result != 0) || ...));
1178-
return __result;
1179-
}
1180-
11811256
template <class... _Tp, class... _Up>
11821257
requires(sizeof...(_Tp) == sizeof...(_Up))
11831258
_LIBCPP_HIDE_FROM_ABI constexpr common_comparison_category_t<__synth_three_way_result<_Tp, _Up>...>
11841259
operator<=>(const tuple<_Tp...>& __x, const tuple<_Up...>& __y) {
1185-
return std::__tuple_compare_three_way(__x, __y, index_sequence_for<_Tp...>{});
1260+
return std::__tuple_compare_three_way<common_comparison_category_t<__synth_three_way_result<_Tp, _Up>...>>(
1261+
__x, __y, index_sequence_for<_Tp...>{});
11861262
}
11871263

11881264
# else // _LIBCPP_STD_VER >= 20

0 commit comments

Comments
 (0)