diff options
author | Jonathan Wakely <jwakely@redhat.com> | 2024-03-27 15:24:05 +0000 |
---|---|---|
committer | Jonathan Wakely <jwakely@redhat.com> | 2024-05-07 13:46:11 +0100 |
commit | b72e7addf855aed66c0922f17efc85f26193f801 (patch) | |
tree | eedb5a1e728233d8c6fb4a91fbadf3f486b96405 /libstdc++-v3/include/std/tuple | |
parent | 9ebd123432873edf1551006be07381150fd617ea (diff) | |
download | gcc-b72e7addf855aed66c0922f17efc85f26193f801.zip gcc-b72e7addf855aed66c0922f17efc85f26193f801.tar.gz gcc-b72e7addf855aed66c0922f17efc85f26193f801.tar.bz2 |
libstdc++: Constrain equality ops for std::pair, std::tuple, std::variant
Implement the changes from P2944R3 which add constraints to the
comparison operators of std::pair, std::tuple, and std::variant.
The paper also changes std::optional, but we already constrain its
comparisons using SFINAE on the return type. However, we need some
additional constraints on the [optional.comp.with.t] operators that
compare an optional with a value. The paper doesn't say to do that, but
I think it's needed because otherwise when the comparison for two
optional objects fails its constraints, the two overloads that are
supposed to be for comparing to a non-optional become the best overload
candidates, but are ambiguous (and we don't even get as far as checking
the constraints for satisfaction). I reported LWG 4072 for this.
The paper does not change std::expected, but probably should have done.
I'll submit an LWG issue about that and implement it separately.
Also add [[nodiscard]] to all these comparison operators.
libstdc++-v3/ChangeLog:
* include/bits/stl_pair.h (operator==): Add constraint.
* include/bits/version.def (constrained_equality): Define.
* include/bits/version.h: Regenerate.
* include/std/optional: Define feature test macro.
(__optional_rep_op_t): Use is_convertible_v instead of
is_convertible.
* include/std/tuple: Define feature test macro.
(operator==, __tuple_cmp, operator<=>): Reimplement C++20
comparisons using lambdas. Add constraints.
* include/std/utility: Define feature test macro.
* include/std/variant: Define feature test macro.
(_VARIANT_RELATION_FUNCTION_TEMPLATE): Add constraints.
(variant): Remove unnecessary friend declarations for comparison
operators.
* testsuite/20_util/optional/relops/constrained.cc: New test.
* testsuite/20_util/pair/comparison_operators/constrained.cc:
New test.
* testsuite/20_util/tuple/comparison_operators/constrained.cc:
New test.
* testsuite/20_util/variant/relops/constrained.cc: New test.
* testsuite/20_util/tuple/comparison_operators/overloaded.cc:
Disable for C++20 and later.
* testsuite/20_util/tuple/comparison_operators/overloaded2.cc:
Remove dg-error line for target c++20.
Diffstat (limited to 'libstdc++-v3/include/std/tuple')
-rw-r--r-- | libstdc++-v3/include/std/tuple | 102 |
1 files changed, 63 insertions, 39 deletions
diff --git a/libstdc++-v3/include/std/tuple b/libstdc++-v3/include/std/tuple index 3065058..df3f6e3 100644 --- a/libstdc++-v3/include/std/tuple +++ b/libstdc++-v3/include/std/tuple @@ -51,6 +51,7 @@ #define __glibcxx_want_make_from_tuple #define __glibcxx_want_ranges_zip #define __glibcxx_want_tuple_like +#define __glibcxx_want_constrained_equality #include <bits/version.h> namespace std _GLIBCXX_VISIBILITY(default) @@ -250,17 +251,11 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION #if __cpp_lib_tuple_like // >= C++23 struct __tuple_like_tag_t { explicit __tuple_like_tag_t() = default; }; - // These forward declarations are used by the operator<=> overload for + // This forward declaration is used by the operator<=> overload for // tuple-like types. - template<typename _Cat, typename _Tp, typename _Up> + template<typename _Cat, typename _Tp, typename _Up, typename _IndexSeq> constexpr _Cat - __tuple_cmp(const _Tp&, const _Up&, index_sequence<>); - - template<typename _Cat, typename _Tp, typename _Up, - size_t _Idx0, size_t... _Idxs> - constexpr _Cat - __tuple_cmp(const _Tp& __t, const _Up& __u, - index_sequence<_Idx0, _Idxs...>); + __tuple_cmp(const _Tp& __t, const _Up& __u, _IndexSeq); #endif // C++23 /** @@ -1848,7 +1843,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION template<__tuple_like _UTuple> requires (!__is_tuple_v<_UTuple>) friend constexpr bool - operator==(const tuple& __t, const _UTuple& __u) + operator== [[nodiscard]] (const tuple& __t, const _UTuple& __u) { static_assert(sizeof...(_Elements) == tuple_size_v<_UTuple>, "tuple objects can only be compared if they have equal sizes."); @@ -2521,6 +2516,58 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } #endif +#if __cpp_lib_three_way_comparison + template<typename... _Tps, typename... _Ups> + requires (sizeof...(_Tps) == sizeof...(_Ups)) + && (requires (const _Tps& __t, const _Ups& __u) { + { __t == __u } -> __detail::__boolean_testable; + } && ...) + constexpr bool + operator== [[nodiscard]] (const tuple<_Tps...>& __t, + const tuple<_Ups...>& __u) + { + return [&]<size_t... _Inds>(index_sequence<_Inds...>) { + // Fold == over the tuples until non-equal elements are found. + return ((std::get<_Inds>(__t) == std::get<_Inds>(__u)) && ...); + }(index_sequence_for<_Tps...>{}); + } + + template<typename _Cat, typename _Tp, typename _Up, typename _IndexSeq> + [[nodiscard]] + constexpr _Cat + __tuple_cmp(const _Tp& __t, const _Up& __u, _IndexSeq __indices) + { + _Cat __c = _Cat::equivalent; + + // Set __c to the comparison result of two corresponding elements. + // Return true they are equivalent. + auto __cmp = [&]<size_t _Ind>(integral_constant<size_t, _Ind>) { + __c = __detail::__synth3way(std::get<_Ind>(__t), std::get<_Ind>(__u)); + return __c == 0; + }; + + [&]<size_t... _Inds>(index_sequence<_Inds...>) { + // Fold __cmp over the tuples until non-equivalent elements are found. + (void)(__cmp(integral_constant<size_t, _Inds>{}) && ...); + }(__indices); + + return __c; + } + + template<typename... _Tps, typename... _Ups> + requires (sizeof...(_Tps) == sizeof...(_Ups)) + && (requires { typename __detail::__synth3way_t<_Tps, _Ups>; } && ...) + constexpr + common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...> + operator<=> [[nodiscard]] (const tuple<_Tps...>& __t, + const tuple<_Ups...>& __u) + { + using _Cat + = common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...>; + return std::__tuple_cmp<_Cat>(__t, __u, index_sequence_for<_Tps...>()); + } +#else + // This class performs the comparison operations on tuples template<typename _Tp, typename _Up, size_t __i, size_t __size> struct __tuple_compare @@ -2552,6 +2599,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION }; template<typename... _TElements, typename... _UElements> + _GLIBCXX_NODISCARD constexpr bool operator==(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u) @@ -2564,36 +2612,8 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION return __compare::__eq(__t, __u); } -#if __cpp_lib_three_way_comparison - template<typename _Cat, typename _Tp, typename _Up> - constexpr _Cat - __tuple_cmp(const _Tp&, const _Up&, index_sequence<>) - { return _Cat::equivalent; } - - template<typename _Cat, typename _Tp, typename _Up, - size_t _Idx0, size_t... _Idxs> - constexpr _Cat - __tuple_cmp(const _Tp& __t, const _Up& __u, - index_sequence<_Idx0, _Idxs...>) - { - auto __c - = __detail::__synth3way(std::get<_Idx0>(__t), std::get<_Idx0>(__u)); - if (__c != 0) - return __c; - return std::__tuple_cmp<_Cat>(__t, __u, index_sequence<_Idxs...>()); - } - - template<typename... _Tps, typename... _Ups> - constexpr - common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...> - operator<=>(const tuple<_Tps...>& __t, const tuple<_Ups...>& __u) - { - using _Cat - = common_comparison_category_t<__detail::__synth3way_t<_Tps, _Ups>...>; - return std::__tuple_cmp<_Cat>(__t, __u, index_sequence_for<_Tps...>()); - } -#else template<typename... _TElements, typename... _UElements> + _GLIBCXX_NODISCARD constexpr bool operator<(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u) @@ -2607,24 +2627,28 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION } template<typename... _TElements, typename... _UElements> + _GLIBCXX_NODISCARD constexpr bool operator!=(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u) { return !(__t == __u); } template<typename... _TElements, typename... _UElements> + _GLIBCXX_NODISCARD constexpr bool operator>(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u) { return __u < __t; } template<typename... _TElements, typename... _UElements> + _GLIBCXX_NODISCARD constexpr bool operator<=(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u) { return !(__u < __t); } template<typename... _TElements, typename... _UElements> + _GLIBCXX_NODISCARD constexpr bool operator>=(const tuple<_TElements...>& __t, const tuple<_UElements...>& __u) |