[libcxx-commits] [libcxx] f8b5ac3 - [libc++][spaceship] Implement `operator<=>` for `multiset` and `set`
Hristo Hristov via libcxx-commits
libcxx-commits at lists.llvm.org
Sun May 28 23:00:21 PDT 2023
Author: Hristo Hristov
Date: 2023-05-29T09:00:14+03:00
New Revision: f8b5ac34adb9d6e60202d8cc92d9c3cd02806c66
URL: https://github.com/llvm/llvm-project/commit/f8b5ac34adb9d6e60202d8cc92d9c3cd02806c66
DIFF: https://github.com/llvm/llvm-project/commit/f8b5ac34adb9d6e60202d8cc92d9c3cd02806c66.diff
LOG: [libc++][spaceship] Implement `operator<=>` for `multiset` and `set`
Implements parts of P1614R2
Implemented `operator<=>` for `multiset` and `set`
Reviewed By: #libc, Mordante
Differential Revision: https://reviews.llvm.org/D148416
Added:
libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.verify.cpp
libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.verify.cpp
Modified:
libcxx/include/set
libcxx/test/support/test_comparisons.h
libcxx/test/support/test_container_comparisons.h
Removed:
################################################################################
diff --git a/libcxx/include/set b/libcxx/include/set
index b0e5e50779d29..7d54c9022a8b6 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -210,27 +210,31 @@ operator==(const set<Key, Compare, Allocator>& x,
template <class Key, class Compare, class Allocator>
bool
operator< (const set<Key, Compare, Allocator>& x,
- const set<Key, Compare, Allocator>& y);
+ const set<Key, Compare, Allocator>& y); // removed in C++20
template <class Key, class Compare, class Allocator>
bool
operator!=(const set<Key, Compare, Allocator>& x,
- const set<Key, Compare, Allocator>& y);
+ const set<Key, Compare, Allocator>& y); // removed in C++20
template <class Key, class Compare, class Allocator>
bool
operator> (const set<Key, Compare, Allocator>& x,
- const set<Key, Compare, Allocator>& y);
+ const set<Key, Compare, Allocator>& y); // removed in C++20
template <class Key, class Compare, class Allocator>
bool
operator>=(const set<Key, Compare, Allocator>& x,
- const set<Key, Compare, Allocator>& y);
+ const set<Key, Compare, Allocator>& y); // removed in C++20
template <class Key, class Compare, class Allocator>
bool
operator<=(const set<Key, Compare, Allocator>& x,
- const set<Key, Compare, Allocator>& y);
+ const set<Key, Compare, Allocator>& y); // removed in C++20
+
+template<class Key, class Compare, class Allocator>
+ synth-three-way-result<Key> operator<=>(const set<Key, Compare, Allocator>& x,
+ const set<Key, Compare, Allocator>& y); // since C++20
// specialized algorithms:
template <class Key, class Compare, class Allocator>
@@ -435,27 +439,31 @@ operator==(const multiset<Key, Compare, Allocator>& x,
template <class Key, class Compare, class Allocator>
bool
operator< (const multiset<Key, Compare, Allocator>& x,
- const multiset<Key, Compare, Allocator>& y);
+ const multiset<Key, Compare, Allocator>& y); // removed in C++20
template <class Key, class Compare, class Allocator>
bool
operator!=(const multiset<Key, Compare, Allocator>& x,
- const multiset<Key, Compare, Allocator>& y);
+ const multiset<Key, Compare, Allocator>& y); // removed in C++20
template <class Key, class Compare, class Allocator>
bool
operator> (const multiset<Key, Compare, Allocator>& x,
- const multiset<Key, Compare, Allocator>& y);
+ const multiset<Key, Compare, Allocator>& y); // removed in C++20
template <class Key, class Compare, class Allocator>
bool
operator>=(const multiset<Key, Compare, Allocator>& x,
- const multiset<Key, Compare, Allocator>& y);
+ const multiset<Key, Compare, Allocator>& y); // removed in C++20
template <class Key, class Compare, class Allocator>
bool
operator<=(const multiset<Key, Compare, Allocator>& x,
- const multiset<Key, Compare, Allocator>& y);
+ const multiset<Key, Compare, Allocator>& y); // removed in C++20
+
+template<class Key, class Compare, class Allocator>
+ synth-three-way-result<Key> operator<=>(const multiset<Key, Compare, Allocator>& x,
+ const multiset<Key, Compare, Allocator>& y); // since C++20
// specialized algorithms:
template <class Key, class Compare, class Allocator>
@@ -473,6 +481,7 @@ erase_if(multiset<Key, Compare, Allocator>& c, Predicate pred); // C++20
#include <__algorithm/equal.h>
#include <__algorithm/lexicographical_compare.h>
+#include <__algorithm/lexicographical_compare_three_way.h>
#include <__assert> // all public C++ headers provide the assertion handler
#include <__config>
#include <__functional/is_transparent.h>
@@ -982,6 +991,8 @@ operator==(const set<_Key, _Compare, _Allocator>& __x,
return __x.size() == __y.size() && _VSTD::equal(__x.begin(), __x.end(), __y.begin());
}
+#if _LIBCPP_STD_VER <= 17
+
template <class _Key, class _Compare, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
bool
@@ -1027,6 +1038,17 @@ operator<=(const set<_Key, _Compare, _Allocator>& __x,
return !(__y < __x);
}
+#else // _LIBCPP_STD_VER <= 17
+
+template <class _Key, class _Allocator>
+_LIBCPP_HIDE_FROM_ABI __synth_three_way_result<_Key>
+operator<=>(const set<_Key, _Allocator>& __x, const set<_Key, _Allocator>& __y) {
+ return std::lexicographical_compare_three_way(
+ __x.begin(), __x.end(), __y.begin(), __y.end(), std::__synth_three_way<_Key, _Key>);
+}
+
+#endif // _LIBCPP_STD_VER <= 17
+
// specialized algorithms:
template <class _Key, class _Compare, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
@@ -1518,6 +1540,8 @@ operator==(const multiset<_Key, _Compare, _Allocator>& __x,
return __x.size() == __y.size() && _VSTD::equal(__x.begin(), __x.end(), __y.begin());
}
+#if _LIBCPP_STD_VER <= 17
+
template <class _Key, class _Compare, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
bool
@@ -1563,6 +1587,17 @@ operator<=(const multiset<_Key, _Compare, _Allocator>& __x,
return !(__y < __x);
}
+#else // _LIBCPP_STD_VER <= 17
+
+template <class _Key, class _Allocator>
+_LIBCPP_HIDE_FROM_ABI __synth_three_way_result<_Key>
+operator<=>(const multiset<_Key, _Allocator>& __x, const multiset<_Key, _Allocator>& __y) {
+ return std::lexicographical_compare_three_way(
+ __x.begin(), __x.end(), __y.begin(), __y.end(), std::__synth_three_way<_Key, _Key>);
+}
+
+#endif // _LIBCPP_STD_VER <= 17
+
template <class _Key, class _Compare, class _Allocator>
inline _LIBCPP_INLINE_VISIBILITY
void
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
new file mode 100644
index 0000000000000..fb575c880dd2e
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <set>
+
+// class multiset
+
+// template<class Key, class Compare, class Allocator>
+// synth-three-way-result<Key> operator<=>(const multiset<Key, Compare, Allocator>& x,
+// const multiset<Key, Compare, Allocator>& y);
+
+#include <cassert>
+#include <set>
+
+#include "test_container_comparisons.h"
+
+int main(int, char**) {
+ assert(test_ordered_set_container_spaceship<std::multiset>());
+ // `std::multiset` is not constexpr, so no `static_assert` test here.
+ return 0;
+}
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.verify.cpp b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.verify.cpp
new file mode 100644
index 0000000000000..9bde360748f32
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/multiset.nonmember/compare.three_way.verify.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <set>
+
+// class set
+
+// template<class Key, class Compare, class Allocator>
+// synth-three-way-result<Key> operator<=>(const set<Key, Compare, Allocator>& x,
+// const set<Key, Compare, Allocator>& y);
+
+#include <set>
+
+#include "test_allocator.h"
+
+int main(int, char**) {
+ // Mismatching allocators
+ {
+ std::multiset<int, std::less<int>, std::allocator<int>> s1;
+ std::multiset<int, std::less<int>, test_allocator<int>> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s1 <=> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s2 <=> s1;
+ }
+ // Mismatching comparision functions
+ {
+ std::multiset<int, std::less<int>> s1;
+ std::multiset<int, std::greater<int>> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s1 <=> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s2 <=> s1;
+ }
+ {
+ std::multiset<int, std::less<int>> s1;
+ std::multiset<int, std::less<float>> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s1 <=> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s2 <=> s1;
+ }
+ // Mismatching types
+ {
+ std::multiset<int> s1;
+ std::multiset<float> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s1 <=> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s2 <=> s1;
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
new file mode 100644
index 0000000000000..dc513d96e5ba8
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.pass.cpp
@@ -0,0 +1,27 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <set>
+
+// class set
+
+// template<class Key, class Compare, class Allocator>
+// synth-three-way-result<Key> operator<=>(const set<Key, Compare, Allocator>& x,
+// const set<Key, Compare, Allocator>& y);
+
+#include <cassert>
+#include <set>
+
+#include "test_container_comparisons.h"
+
+int main(int, char**) {
+ assert((test_ordered_set_container_spaceship<std::set>()));
+ // `std::set` is not constexpr, so no `static_assert` test here.
+ return 0;
+}
diff --git a/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.verify.cpp b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.verify.cpp
new file mode 100644
index 0000000000000..a2d8c4e44e4e5
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/set.nonmember/compare.three_way.verify.cpp
@@ -0,0 +1,60 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+// UNSUPPORTED: c++03, c++11, c++14, c++17
+
+// <set>
+
+// class set
+
+// template<class Key, class Compare, class Allocator>
+// synth-three-way-result<Key> operator<=>(const set<Key, Compare, Allocator>& x,
+// const set<Key, Compare, Allocator>& y);
+
+#include <set>
+
+#include "test_allocator.h"
+
+int main(int, char**) {
+ // Mismatching allocators
+ {
+ std::set<int, std::less<int>, std::allocator<int>> s1;
+ std::set<int, std::less<int>, test_allocator<int>> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s1 <=> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s2 <=> s1;
+ }
+ // Mismatching comparision functions
+ {
+ std::set<int, std::less<int>> s1;
+ std::set<int, std::greater<int>> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s1 <=> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s2 <=> s1;
+ }
+ {
+ std::set<int, std::less<int>> s1;
+ std::set<int, std::less<float>> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s1 <=> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s2 <=> s1;
+ }
+ // Mismatching types
+ {
+ std::set<int> s1;
+ std::set<float> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s1 <=> s2;
+ // expected-error at +1 {{invalid operands to binary expression}}
+ s2 <=> s1;
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/support/test_comparisons.h b/libcxx/test/support/test_comparisons.h
index c54e877339d93..e006f69f8bf67 100644
--- a/libcxx/test/support/test_comparisons.h
+++ b/libcxx/test/support/test_comparisons.h
@@ -241,7 +241,8 @@ struct LessAndEqComp {
}
};
-#if TEST_STD_VER > 17
+#if TEST_STD_VER >= 20
+
struct StrongOrder {
int value;
constexpr StrongOrder(int v) : value(v) {}
@@ -260,6 +261,8 @@ struct PartialOrder {
friend constexpr std::partial_ordering operator<=>(PartialOrder lhs, PartialOrder rhs) {
if (lhs.value == std::numeric_limits<int>::min() || rhs.value == std::numeric_limits<int>::min())
return std::partial_ordering::unordered;
+ if (lhs.value == std::numeric_limits<int>::max() || rhs.value == std::numeric_limits<int>::max())
+ return std::partial_ordering::unordered;
return lhs.value <=> rhs.value;
}
friend constexpr bool operator==(PartialOrder lhs, PartialOrder rhs) {
diff --git a/libcxx/test/support/test_container_comparisons.h b/libcxx/test/support/test_container_comparisons.h
index f9dae9d6455ad..d3b4033039c37 100644
--- a/libcxx/test/support/test_container_comparisons.h
+++ b/libcxx/test/support/test_container_comparisons.h
@@ -10,6 +10,8 @@
#ifndef TEST_CONTAINER_COMPARISONS
#define TEST_CONTAINER_COMPARISONS
+#include <set>
+
#include "test_comparisons.h"
// Implementation detail of `test_sequence_container_spaceship`
@@ -183,7 +185,7 @@ constexpr void test_ordered_map_container_spaceship_with_type() {
}
}
-// Tests the `operator<=>` on ordered containers
+// Tests the `operator<=>` on ordered map containers
template <template <typename...> typename Container>
constexpr bool test_ordered_map_container_spaceship() {
// The container should fulfill `std::three_way_comparable`
@@ -205,4 +207,107 @@ constexpr bool test_ordered_map_container_spaceship() {
return true;
}
+// Implementation detail of `test_ordered_set_container_spaceship`
+template <template <typename...> typename Container, typename Elem, typename Order, typename Compare>
+constexpr void test_ordered_set_spaceship_with_type(Compare comp) {
+ // Empty containers
+ {
+ Container<Elem, Compare> l1{{}, comp};
+ Container<Elem, Compare> l2{{}, comp};
+ assert(testOrder(l1, l2, Order::equivalent));
+ }
+ // Identical contents
+ {
+ Container<Elem, Compare> l1{{1, 1, 2}, comp};
+ Container<Elem, Compare> l2{{1, 1, 2}, comp};
+ assert(testOrder(l1, l2, Order::equivalent));
+ }
+ // Less, due to contained values
+ {
+ Container<Elem, Compare> l1{{1, 1, 2, 3}, comp};
+ Container<Elem, Compare> l2{{1, 2, 2, 4}, comp};
+ assert(testOrder(l1, l2, Order::less));
+ }
+ // Greater, due to contained values
+ {
+ Container<Elem, Compare> l1{{1, 2, 2, 4}, comp};
+ Container<Elem, Compare> l2{{1, 1, 2, 3}, comp};
+ assert(testOrder(l1, l2, Order::greater));
+ }
+ // Shorter list
+ {
+ Container<Elem, Compare> l1{{1, 1, 2, 2}, comp};
+ Container<Elem, Compare> l2{{1, 1, 2, 2, 3}, comp};
+ assert(testOrder(l1, l2, Order::less));
+ }
+ // Longer list
+ {
+ Container<Elem, Compare> l1{{1, 1, 2, 2, 3}, comp};
+ Container<Elem, Compare> l2{{1, 1, 2, 2}, comp};
+ assert(testOrder(l1, l2, Order::greater));
+ }
+ // Unordered
+ if constexpr (std::is_same_v< Container<Elem>, std::multiset<PartialOrder>>) {
+ if constexpr (std::is_same_v<Elem, PartialOrder> && std::is_same_v<Compare, decltype(std::less{})>) {
+ Container<Elem, Compare> l1{{1, std::numeric_limits<int>::min()}, comp};
+ Container<Elem, Compare> l2{{1, 2}, comp};
+ assert(testOrder(l1, l2, Order::unordered));
+ }
+ if constexpr (std::is_same_v<Elem, PartialOrder> && std::is_same_v<Compare, decltype(std::less{})>) {
+ Container<Elem, Compare> l1{{1, std::numeric_limits<int>::max()}, comp};
+ Container<Elem, Compare> l2{{1, 2}, comp};
+ assert(testOrder(l1, l2, Order::unordered));
+ }
+ }
+ if constexpr (std::is_same_v< Container<Elem>, std::set<PartialOrder>>) {
+ // Unodered values are not supported for `set`
+ if constexpr (std::is_same_v<Elem, PartialOrder> && std::is_same_v<Compare, decltype(std::less{})>) {
+ Container<Elem, Compare> l1{{1, std::numeric_limits<int>::min()}, comp};
+ Container<Elem, Compare> l2{{1, 2}, comp};
+ assert(testOrder(l1, l2, Order::less));
+ }
+ if constexpr (std::is_same_v<Elem, PartialOrder> && std::is_same_v<Compare, decltype(std::less{})>) {
+ Container<Elem, Compare> l1{{1, std::numeric_limits<int>::max()}, comp};
+ Container<Elem, Compare> l2{{1, 2}, comp};
+ assert(testOrder(l1, l2, Order::less));
+ }
+ }
+ if constexpr (std::is_same_v<Elem, PartialOrder> && std::is_same_v<Compare, decltype(std::greater{})>) {
+ Container<Elem, Compare> l1{{1, std::numeric_limits<int>::min()}, comp};
+ Container<Elem, Compare> l2{{1, 2}, comp};
+ assert(testOrder(l1, l2, Order::less));
+ }
+ if constexpr (std::is_same_v<Elem, PartialOrder> && std::is_same_v<Compare, decltype(std::greater{})>) {
+ Container<Elem, Compare> l1{{1, std::numeric_limits<int>::max()}, comp};
+ Container<Elem, Compare> l2{{1, 2}, comp};
+ assert(testOrder(l1, l2, Order::less));
+ }
+}
+
+// Tests the `operator<=>` on ordered set containers
+template <template <typename...> typename Container>
+constexpr bool test_ordered_set_container_spaceship() {
+ // Thanks to SFINAE, the following is not a compiler error but returns `false`
+ struct NonComparable {};
+ static_assert(!std::three_way_comparable<Container<NonComparable>>);
+
+ // The container should fulfill `std::three_way_comparable`
+ static_assert(std::three_way_comparable<Container<int>>);
+
+ // Test
diff erent comparison categories
+ test_ordered_set_spaceship_with_type<Container, int, std::strong_ordering>(std::less{});
+ test_ordered_set_spaceship_with_type<Container, int, std::strong_ordering>(std::greater{});
+ test_ordered_set_spaceship_with_type<Container, StrongOrder, std::strong_ordering>(std::less{});
+ test_ordered_set_spaceship_with_type<Container, StrongOrder, std::strong_ordering>(std::greater{});
+ test_ordered_set_spaceship_with_type<Container, WeakOrder, std::weak_ordering>(std::less{});
+ test_ordered_set_spaceship_with_type<Container, WeakOrder, std::weak_ordering>(std::greater{});
+ test_ordered_set_spaceship_with_type<Container, PartialOrder, std::partial_ordering>(std::less{});
+ test_ordered_set_spaceship_with_type<Container, PartialOrder, std::partial_ordering>(std::greater{});
+
+ // `LessAndEqComp` does not have `operator<=>`. Ordering is synthesized based on `operator<`
+ test_ordered_set_spaceship_with_type<Container, LessAndEqComp, std::weak_ordering>(std::less{});
+
+ return true;
+}
+
#endif
More information about the libcxx-commits
mailing list