xref: /llvm-project/libcxx/test/std/utilities/variant/variant.relops/three_way.pass.cpp (revision dd9afdbb2ab47ad09e36c0afc94049285b474264)
1 //===----------------------------------------------------------------------===//
2 //
3 // Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
4 // See https://llvm.org/LICENSE.txt for license information.
5 // SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
6 //
7 //===----------------------------------------------------------------------===//
8 
9 // UNSUPPORTED: c++03, c++11, c++14, c++17
10 
11 // <variant>
12 
13 // template <class... Types> class variant;
14 
15 // template <class... Types> requires (three_way_comparable<Types> && ...)
16 //   constexpr std::common_comparison_category_t<
17 //     std::compare_three_way_result_t<Types>...>
18 //   operator<=>(const variant<Types...>& t, const variant<Types...>& u);
19 
20 #include <cassert>
21 #include <limits>
22 #include <type_traits>
23 #include <utility>
24 #include <variant>
25 
26 #include "test_macros.h"
27 #include "test_comparisons.h"
28 
29 #ifndef TEST_HAS_NO_EXCEPTIONS
30 // MakeEmptyT throws in operator=(&&), so we can move to it to create valueless-by-exception variants.
31 struct MakeEmptyT {
32   MakeEmptyT() = default;
MakeEmptyTMakeEmptyT33   MakeEmptyT(MakeEmptyT&&) { throw 42; }
operator =MakeEmptyT34   MakeEmptyT& operator=(MakeEmptyT&&) { throw 42; }
35 };
operator ==(const MakeEmptyT &,const MakeEmptyT &)36 inline bool operator==(const MakeEmptyT&, const MakeEmptyT&) {
37   assert(false);
38   return false;
39 }
operator <=>(const MakeEmptyT &,const MakeEmptyT &)40 inline std::weak_ordering operator<=>(const MakeEmptyT&, const MakeEmptyT&) {
41   assert(false);
42   return std::weak_ordering::equivalent;
43 }
44 
45 template <class Variant>
makeEmpty(Variant & v)46 void makeEmpty(Variant& v) {
47   Variant v2(std::in_place_type<MakeEmptyT>);
48   try {
49     v = std::move(v2);
50     assert(false);
51   } catch (...) {
52     assert(v.valueless_by_exception());
53   }
54 }
55 
test_empty()56 void test_empty() {
57   {
58     using V = std::variant<int, MakeEmptyT>;
59     V v1;
60     V v2;
61     makeEmpty(v2);
62     assert(testOrder(v1, v2, std::weak_ordering::greater));
63   }
64   {
65     using V = std::variant<int, MakeEmptyT>;
66     V v1;
67     makeEmpty(v1);
68     V v2;
69     assert(testOrder(v1, v2, std::weak_ordering::less));
70   }
71   {
72     using V = std::variant<int, MakeEmptyT>;
73     V v1;
74     makeEmpty(v1);
75     V v2;
76     makeEmpty(v2);
77     assert(testOrder(v1, v2, std::weak_ordering::equivalent));
78   }
79 }
80 #endif // TEST_HAS_NO_EXCEPTIONS
81 
82 template <class T1, class T2, class Order>
test_with_types()83 constexpr bool test_with_types() {
84   using V = std::variant<T1, T2>;
85   AssertOrderReturn<Order, V>();
86   { // same index, same value
87     constexpr V v1(std::in_place_index<0>, T1{1});
88     constexpr V v2(std::in_place_index<0>, T1{1});
89     assert(testOrder(v1, v2, Order::equivalent));
90   }
91   { // same index, value < other_value
92     constexpr V v1(std::in_place_index<0>, T1{0});
93     constexpr V v2(std::in_place_index<0>, T1{1});
94     assert(testOrder(v1, v2, Order::less));
95   }
96   { // same index, value > other_value
97     constexpr V v1(std::in_place_index<0>, T1{1});
98     constexpr V v2(std::in_place_index<0>, T1{0});
99     assert(testOrder(v1, v2, Order::greater));
100   }
101   { // LHS.index() < RHS.index()
102     constexpr V v1(std::in_place_index<0>, T1{0});
103     constexpr V v2(std::in_place_index<1>, T2{0});
104     assert(testOrder(v1, v2, Order::less));
105   }
106   { // LHS.index() > RHS.index()
107     constexpr V v1(std::in_place_index<1>, T2{0});
108     constexpr V v2(std::in_place_index<0>, T1{0});
109     assert(testOrder(v1, v2, Order::greater));
110   }
111 
112   return true;
113 }
114 
test_three_way()115 constexpr bool test_three_way() {
116   assert((test_with_types<int, double, std::partial_ordering>()));
117   assert((test_with_types<int, long, std::strong_ordering>()));
118 
119   {
120     using V              = std::variant<int, double>;
121     constexpr double nan = std::numeric_limits<double>::quiet_NaN();
122     {
123       constexpr V v1(std::in_place_type<int>, 1);
124       constexpr V v2(std::in_place_type<double>, nan);
125       assert(testOrder(v1, v2, std::partial_ordering::less));
126     }
127     {
128       constexpr V v1(std::in_place_type<double>, nan);
129       constexpr V v2(std::in_place_type<int>, 2);
130       assert(testOrder(v1, v2, std::partial_ordering::greater));
131     }
132     {
133       constexpr V v1(std::in_place_type<double>, nan);
134       constexpr V v2(std::in_place_type<double>, nan);
135       assert(testOrder(v1, v2, std::partial_ordering::unordered));
136     }
137   }
138 
139   return true;
140 }
141 
142 // SFINAE tests
143 template <class T, class U = T>
144 concept has_three_way_op = requires (T& t, U& u) { t <=> u; };
145 
146 // std::three_way_comparable is a more stringent requirement that demands
147 // operator== and a few other things.
148 using std::three_way_comparable;
149 
150 struct HasSimpleOrdering {
151   constexpr bool operator==(const HasSimpleOrdering&) const;
152   constexpr bool operator<(const HasSimpleOrdering&) const;
153 };
154 
155 struct HasOnlySpaceship {
156   constexpr bool operator==(const HasOnlySpaceship&) const = delete;
157   constexpr std::weak_ordering operator<=>(const HasOnlySpaceship&) const;
158 };
159 
160 struct HasFullOrdering {
161   constexpr bool operator==(const HasFullOrdering&) const;
162   constexpr std::weak_ordering operator<=>(const HasFullOrdering&) const;
163 };
164 
165 // operator<=> must resolve the return types of all its union types'
166 // operator<=>s to determine its own return type, so it is detectable by SFINAE
167 static_assert(!has_three_way_op<HasSimpleOrdering>);
168 static_assert(!has_three_way_op<std::variant<int, HasSimpleOrdering>>);
169 
170 static_assert(!three_way_comparable<HasSimpleOrdering>);
171 static_assert(!three_way_comparable<std::variant<int, HasSimpleOrdering>>);
172 
173 static_assert(has_three_way_op<HasOnlySpaceship>);
174 static_assert(!has_three_way_op<std::variant<int, HasOnlySpaceship>>);
175 
176 static_assert(!three_way_comparable<HasOnlySpaceship>);
177 static_assert(!three_way_comparable<std::variant<int, HasOnlySpaceship>>);
178 
179 static_assert( has_three_way_op<HasFullOrdering>);
180 static_assert( has_three_way_op<std::variant<int, HasFullOrdering>>);
181 
182 static_assert( three_way_comparable<HasFullOrdering>);
183 static_assert( three_way_comparable<std::variant<int, HasFullOrdering>>);
184 
main(int,char **)185 int main(int, char**) {
186   test_three_way();
187   static_assert(test_three_way());
188 
189 #ifndef TEST_HAS_NO_EXCEPTIONS
190   test_empty();
191 #endif // TEST_HAS_NO_EXCEPTIONS
192 
193   return 0;
194 }
195