diff --git a/libcxx/include/variant b/libcxx/include/variant --- a/libcxx/include/variant +++ b/libcxx/include/variant @@ -165,6 +165,10 @@ template constexpr bool operator>=(const variant&, const variant&); + template + constexpr common_comparison_category_t...> + operator<=>(const variant&, const variant&); // since C++20 + // 20.7.6, visitation template constexpr see below visit(Visitor&&, Variants&&...); @@ -201,6 +205,9 @@ #include <__assert> // all public C++ headers provide the assertion handler #include <__availability> +#include <__compare/common_comparison_category.h> +#include <__compare/compare_three_way_result.h> +#include <__compare/three_way_comparable.h> #include <__config> #include <__functional/hash.h> #include <__functional/operations.h> @@ -1630,6 +1637,27 @@ return __variant::__visit_value_at(__lhs.index(), __convert_to_bool>{}, __lhs, __rhs); } +#if _LIBCPP_STD_VER > 17 + +template requires (three_way_comparable<_Types> && ...) +inline _LIBCPP_INLINE_VISIBILITY constexpr +common_comparison_category_t...> +operator<=>(const variant<_Types...>& __lhs, + const variant<_Types...>& __rhs) { + using __variant_detail::__visitation::__variant; + if (__lhs.valueless_by_exception() && __rhs.valueless_by_exception()) return strong_ordering::equal; + if (__lhs.valueless_by_exception()) return strong_ordering::less; + if (__rhs.valueless_by_exception()) return strong_ordering::greater; + if (auto __c = __lhs.index() <=> __rhs.index(); __c != 0) return __c; + auto __three_way = [](const _Type& __v, const _Type& __w) -> common_comparison_category_t< + compare_three_way_result_t<_Types>...> { + return __v <=> __w; + }; + return __variant::__visit_value_at(__lhs.index(), __three_way, __lhs, __rhs); +} + +#endif // _LIBCPP_STD_VER > 17 + template inline _LIBCPP_INLINE_VISIBILITY constexpr bool operator!=(const variant<_Types...>& __lhs, diff --git a/libcxx/test/std/utilities/variant/variant.relops/three_way.pass.cpp b/libcxx/test/std/utilities/variant/variant.relops/three_way.pass.cpp new file mode 100644 --- /dev/null +++ b/libcxx/test/std/utilities/variant/variant.relops/three_way.pass.cpp @@ -0,0 +1,133 @@ +//===----------------------------------------------------------------------===// +// +// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions. +// See https://llvm.org/LICENSE.txt for license information. +// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception +// +//===----------------------------------------------------------------------===// + +// + +// template class variant; + +// template +// constexpr std::common_comparison_category_t< +// std::compare_three_way_result_t...> +// operator<=>(const variant& t, const variant& u); + +// UNSUPPORTED: c++03, c++11, c++14, c++17 + +#include +#include +#include +#include + +#include "test_macros.h" +#include "test_comparisons.h" + +#ifndef TEST_HAS_NO_EXCEPTIONS +struct MakeEmptyT { + MakeEmptyT() = default; + MakeEmptyT(MakeEmptyT &&) { throw 42; } + MakeEmptyT &operator=(MakeEmptyT &&) { throw 42; } +}; +inline bool operator==(const MakeEmptyT &, const MakeEmptyT &) { + assert(false); + return false; +} +inline std::weak_ordering operator<=>(const MakeEmptyT&, const MakeEmptyT&) { + assert(false); + return std::weak_ordering::equivalent; +} + +template void makeEmpty(Variant &v) { + Variant v2(std::in_place_type); + try { + v = std::move(v2); + assert(false); + } catch (...) { + assert(v.valueless_by_exception()); + } +} +#endif // TEST_HAS_NO_EXCEPTIONS + +template +constexpr bool test_three_way_val(const Var &l, const Var &r, Order order) { + static_assert(std::is_same_v r), Order>); + return (l <=> r) == order; +} + +template +void test_with_types() { + using V = std::variant; + using Order = std::compare_three_way_result_t; + { // same index, same value + constexpr V v1(std::in_place_index<0>, T1{1}); + constexpr V v2(std::in_place_index<0>, T1{1}); + static_assert(testOrder(v1, v2, Order(std::strong_ordering::equivalent))); + } + { // same index, value < other_value + constexpr V v1(std::in_place_index<0>, T1{0}); + constexpr V v2(std::in_place_index<0>, T1{1}); + static_assert(testOrder(v1, v2, Order(std::strong_ordering::less))); + } + { // same index, value > other_value + constexpr V v1(std::in_place_index<0>, T1{1}); + constexpr V v2(std::in_place_index<0>, T1{0}); + static_assert(testOrder(v1, v2, Order(std::strong_ordering::greater))); + } + { // LHS.index() < RHS.index() + constexpr V v1(std::in_place_index<0>, T1{0}); + constexpr V v2(std::in_place_index<1>, T2{0}); + static_assert(testOrder(v1, v2, Order(std::strong_ordering::less))); + } + { // LHS.index() > RHS.index() + constexpr V v1(std::in_place_index<1>, T2{0}); + constexpr V v2(std::in_place_index<0>, T1{0}); + static_assert(testOrder(v1, v2, Order(std::strong_ordering::greater))); + } +} + +void test_three_way() { + { + using V = std::variant; + ASSERT_SAME_TYPE(std::partial_ordering, std::compare_three_way_result_t); + test_with_types(); + } + { + using V = std::variant; + ASSERT_SAME_TYPE(std::strong_ordering, std::compare_three_way_result_t); + test_with_types(); + } + +#ifndef TEST_HAS_NO_EXCEPTIONS + { + using V = std::variant; + V v1; + V v2; + makeEmpty(v2); + assert(testOrder(v1, v2, std::weak_ordering::greater)); + } + { + using V = std::variant; + V v1; + makeEmpty(v1); + V v2; + assert(testOrder(v1, v2, std::weak_ordering::less)); + } + { + using V = std::variant; + V v1; + makeEmpty(v1); + V v2; + makeEmpty(v2); + assert(testOrder(v1, v2, std::weak_ordering::equivalent)); + } +#endif // TEST_HAS_NO_EXCEPTIONS +} + +int main(int, char**) { + test_three_way(); + + return 0; +}