[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
This commit is contained in:
parent
dd0cf23e4a
commit
f8b5ac34ad
@ -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
|
||||
|
@ -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;
|
||||
}
|
@ -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@+1 {{invalid operands to binary expression}}
|
||||
s1 <=> s2;
|
||||
// expected-error@+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@+1 {{invalid operands to binary expression}}
|
||||
s1 <=> s2;
|
||||
// expected-error@+1 {{invalid operands to binary expression}}
|
||||
s2 <=> s1;
|
||||
}
|
||||
{
|
||||
std::multiset<int, std::less<int>> s1;
|
||||
std::multiset<int, std::less<float>> s2;
|
||||
// expected-error@+1 {{invalid operands to binary expression}}
|
||||
s1 <=> s2;
|
||||
// expected-error@+1 {{invalid operands to binary expression}}
|
||||
s2 <=> s1;
|
||||
}
|
||||
// Mismatching types
|
||||
{
|
||||
std::multiset<int> s1;
|
||||
std::multiset<float> s2;
|
||||
// expected-error@+1 {{invalid operands to binary expression}}
|
||||
s1 <=> s2;
|
||||
// expected-error@+1 {{invalid operands to binary expression}}
|
||||
s2 <=> s1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -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;
|
||||
}
|
@ -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@+1 {{invalid operands to binary expression}}
|
||||
s1 <=> s2;
|
||||
// expected-error@+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@+1 {{invalid operands to binary expression}}
|
||||
s1 <=> s2;
|
||||
// expected-error@+1 {{invalid operands to binary expression}}
|
||||
s2 <=> s1;
|
||||
}
|
||||
{
|
||||
std::set<int, std::less<int>> s1;
|
||||
std::set<int, std::less<float>> s2;
|
||||
// expected-error@+1 {{invalid operands to binary expression}}
|
||||
s1 <=> s2;
|
||||
// expected-error@+1 {{invalid operands to binary expression}}
|
||||
s2 <=> s1;
|
||||
}
|
||||
// Mismatching types
|
||||
{
|
||||
std::set<int> s1;
|
||||
std::set<float> s2;
|
||||
// expected-error@+1 {{invalid operands to binary expression}}
|
||||
s1 <=> s2;
|
||||
// expected-error@+1 {{invalid operands to binary expression}}
|
||||
s2 <=> s1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
@ -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) {
|
||||
|
@ -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 different 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
|
||||
|
Loading…
x
Reference in New Issue
Block a user