Skip to content

Commit bc47a19

Browse files
Zingamphilnik777
authored andcommitted
[libc++][spaceship] Implement operator<=> for map and multimap
Implements parts of P1614R2: `operator<=>` for `map` and `multimap` Reviewed By: #libc, philnik Spies: philnik, libcxx-commits, yaxunl Differential Revision: https://reviews.llvm.org/D145976
1 parent 723a53c commit bc47a19

File tree

5 files changed

+235
-12
lines changed

5 files changed

+235
-12
lines changed

libcxx/docs/Status/SpaceshipProjects.csv

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -40,8 +40,8 @@ Section,Description,Dependencies,Assignee,Complete
4040
| `[forward.list.syn] <https://wg21.link/forward.list.syn>`_ (`general <https://wg21.link/container.opt.reqmts>`_),| `forward_list <https://reviews.llvm.org/D145172>`_,[expos.only.func],Hristo Hristov,|Complete|
4141
| `[list.syn] <https://wg21.link/list.syn>`_ (`general <https://wg21.link/container.opt.reqmts>`_),| `list <https://reviews.llvm.org/D132312>`_,[expos.only.func],Adrian Vogelsgesang,|Complete|
4242
| `[vector.syn] <https://wg21.link/vector.syn>`_ (`general <https://wg21.link/container.opt.reqmts>`_),| `vector <https://reviews.llvm.org/D132268>`_,[expos.only.func],Adrian Vogelsgesang,|In Progress|
43-
| `[associative.map.syn] <https://wg21.link/associative.map.syn>`_ (`general <https://wg21.link/container.opt.reqmts>`_),"| map
44-
| multimap",[expos.only.func],Unassigned,|Not Started|
43+
| `[associative.map.syn] <https://wg21.link/associative.map.syn>`_ (`general <https://wg21.link/container.opt.reqmts>`_),"| `map <https://reviews.llvm.org/D145976>`_
44+
| `multimap <https://reviews.llvm.org/D145976>`_",[expos.only.func],Hristo Hristov,|Complete|
4545
| `[associative.set.syn] <https://wg21.link/associative.set.syn>`_ (`general <https://wg21.link/container.opt.reqmts>`_),"| multiset
4646
| set",[expos.only.func],Unassigned,|Not Started|
4747
| `[queue.ops] <https://wg21.link/queue.ops>`_,| queue,None,Unassigned,|Not Started|

libcxx/include/map

Lines changed: 53 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -250,27 +250,32 @@ operator==(const map<Key, T, Compare, Allocator>& x,
250250
template <class Key, class T, class Compare, class Allocator>
251251
bool
252252
operator< (const map<Key, T, Compare, Allocator>& x,
253-
const map<Key, T, Compare, Allocator>& y);
253+
const map<Key, T, Compare, Allocator>& y); // removed in C++20
254254
255255
template <class Key, class T, class Compare, class Allocator>
256256
bool
257257
operator!=(const map<Key, T, Compare, Allocator>& x,
258-
const map<Key, T, Compare, Allocator>& y);
258+
const map<Key, T, Compare, Allocator>& y); // removed in C++20
259259
260260
template <class Key, class T, class Compare, class Allocator>
261261
bool
262262
operator> (const map<Key, T, Compare, Allocator>& x,
263-
const map<Key, T, Compare, Allocator>& y);
263+
const map<Key, T, Compare, Allocator>& y); // removed in C++20
264264
265265
template <class Key, class T, class Compare, class Allocator>
266266
bool
267267
operator>=(const map<Key, T, Compare, Allocator>& x,
268-
const map<Key, T, Compare, Allocator>& y);
268+
const map<Key, T, Compare, Allocator>& y); // removed in C++20
269269
270270
template <class Key, class T, class Compare, class Allocator>
271271
bool
272272
operator<=(const map<Key, T, Compare, Allocator>& x,
273-
const map<Key, T, Compare, Allocator>& y);
273+
const map<Key, T, Compare, Allocator>& y); // removed in C++20
274+
275+
template<class Key, class T, class Compare, class Allocator>
276+
synth-three-way-result<pair<const Key, T>>
277+
operator<=>(const map<Key, T, Compare, Allocator>& x,
278+
const map<Key, T, Compare, Allocator>& y); // since C++20
274279
275280
// specialized algorithms:
276281
template <class Key, class T, class Compare, class Allocator>
@@ -491,27 +496,32 @@ operator==(const multimap<Key, T, Compare, Allocator>& x,
491496
template <class Key, class T, class Compare, class Allocator>
492497
bool
493498
operator< (const multimap<Key, T, Compare, Allocator>& x,
494-
const multimap<Key, T, Compare, Allocator>& y);
499+
const multimap<Key, T, Compare, Allocator>& y); // removed in C++20
495500
496501
template <class Key, class T, class Compare, class Allocator>
497502
bool
498503
operator!=(const multimap<Key, T, Compare, Allocator>& x,
499-
const multimap<Key, T, Compare, Allocator>& y);
504+
const multimap<Key, T, Compare, Allocator>& y); // removed in C++20
500505
501506
template <class Key, class T, class Compare, class Allocator>
502507
bool
503508
operator> (const multimap<Key, T, Compare, Allocator>& x,
504-
const multimap<Key, T, Compare, Allocator>& y);
509+
const multimap<Key, T, Compare, Allocator>& y); // removed in C++20
505510
506511
template <class Key, class T, class Compare, class Allocator>
507512
bool
508513
operator>=(const multimap<Key, T, Compare, Allocator>& x,
509-
const multimap<Key, T, Compare, Allocator>& y);
514+
const multimap<Key, T, Compare, Allocator>& y); // removed in C++20
510515
511516
template <class Key, class T, class Compare, class Allocator>
512517
bool
513518
operator<=(const multimap<Key, T, Compare, Allocator>& x,
514-
const multimap<Key, T, Compare, Allocator>& y);
519+
const multimap<Key, T, Compare, Allocator>& y); // removed in C++20
520+
521+
template<class Key, class T, class Compare, class Allocator>
522+
synth-three-way-result<pair<const Key, T>>
523+
operator<=>(const multimap<Key, T, Compare, Allocator>& x,
524+
const multimap<Key, T, Compare, Allocator>& y); // since c++20
515525
516526
// specialized algorithms:
517527
template <class Key, class T, class Compare, class Allocator>
@@ -530,6 +540,7 @@ erase_if(multimap<Key, T, Compare, Allocator>& c, Predicate pred); // C++20
530540

531541
#include <__algorithm/equal.h>
532542
#include <__algorithm/lexicographical_compare.h>
543+
#include <__algorithm/lexicographical_compare_three_way.h>
533544
#include <__assert> // all public C++ headers provide the assertion handler
534545
#include <__config>
535546
#include <__functional/binary_function.h>
@@ -1676,6 +1687,8 @@ operator==(const map<_Key, _Tp, _Compare, _Allocator>& __x,
16761687
return __x.size() == __y.size() && _VSTD::equal(__x.begin(), __x.end(), __y.begin());
16771688
}
16781689

1690+
#if _LIBCPP_STD_VER <= 17
1691+
16791692
template <class _Key, class _Tp, class _Compare, class _Allocator>
16801693
inline _LIBCPP_INLINE_VISIBILITY
16811694
bool
@@ -1721,6 +1734,20 @@ operator<=(const map<_Key, _Tp, _Compare, _Allocator>& __x,
17211734
return !(__y < __x);
17221735
}
17231736

1737+
#else // #if _LIBCPP_STD_VER <= 17
1738+
1739+
template<class _Key, class _Tp, class _Compare, class _Allocator>
1740+
inline _LIBCPP_HIDE_FROM_ABI
1741+
__synth_three_way_result<pair<const _Key, _Tp>>
1742+
operator<=>(const map<_Key, _Tp, _Compare, _Allocator>& __x,
1743+
const map<_Key, _Tp, _Compare, _Allocator>& __y)
1744+
{
1745+
return std::lexicographical_compare_three_way(
1746+
__x.begin(), __x.end(), __y.begin(), __y.end(), __synth_three_way);
1747+
}
1748+
1749+
#endif // #if _LIBCPP_STD_VER <= 17
1750+
17241751
template <class _Key, class _Tp, class _Compare, class _Allocator>
17251752
inline _LIBCPP_INLINE_VISIBILITY
17261753
void
@@ -2270,6 +2297,8 @@ operator==(const multimap<_Key, _Tp, _Compare, _Allocator>& __x,
22702297
return __x.size() == __y.size() && _VSTD::equal(__x.begin(), __x.end(), __y.begin());
22712298
}
22722299

2300+
#if _LIBCPP_STD_VER <= 17
2301+
22732302
template <class _Key, class _Tp, class _Compare, class _Allocator>
22742303
inline _LIBCPP_INLINE_VISIBILITY
22752304
bool
@@ -2315,6 +2344,20 @@ operator<=(const multimap<_Key, _Tp, _Compare, _Allocator>& __x,
23152344
return !(__y < __x);
23162345
}
23172346

2347+
#else // #if _LIBCPP_STD_VER <= 17
2348+
2349+
template<class _Key, class _Tp, class _Compare, class _Allocator>
2350+
inline _LIBCPP_HIDE_FROM_ABI
2351+
__synth_three_way_result<pair<const _Key, _Tp>>
2352+
operator<=>(const multimap<_Key, _Tp, _Compare, _Allocator>& __x,
2353+
const multimap<_Key, _Tp, _Compare, _Allocator>& __y)
2354+
{
2355+
return std::lexicographical_compare_three_way(
2356+
__x.begin(), __x.end(), __y.begin(), __y.end(), __synth_three_way);
2357+
}
2358+
2359+
#endif // #if _LIBCPP_STD_VER <= 17
2360+
23182361
template <class _Key, class _Tp, class _Compare, class _Allocator>
23192362
inline _LIBCPP_INLINE_VISIBILITY
23202363
void
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
// UNSUPPORTED: c++03, c++11, c++14, c++17
9+
10+
// <map>
11+
12+
// class map
13+
14+
// template<class Key, class T, class Compare, class Allocator>
15+
// synth-three-way-result<pair<const Key, T>>
16+
// operator<=>(const map<Key, T, Compare, Allocator>& x,
17+
// const map<Key, T, Compare, Allocator>& y);
18+
19+
#include <cassert>
20+
#include <map>
21+
22+
#include "test_container_comparisons.h"
23+
24+
int main(int, char**) {
25+
assert(test_ordered_map_container_spaceship<std::map>());
26+
// `std::map` is not constexpr, so no `static_assert` test here.
27+
return 0;
28+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
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+
// UNSUPPORTED: c++03, c++11, c++14, c++17
9+
10+
// <map>
11+
12+
// class multimap
13+
14+
// template<class Key, class T, class Compare, class Allocator>
15+
// synth-three-way-result<pair<const Key, T>>
16+
// operator<=>(const multimap<Key, T, Compare, Allocator>& x,
17+
// const multimap<Key, T, Compare, Allocator>& y);
18+
19+
#include <cassert>
20+
#include <map>
21+
22+
#include "test_container_comparisons.h"
23+
24+
int main(int, char**) {
25+
assert(test_ordered_map_container_spaceship<std::multimap>());
26+
// `std::multimap` is not constexpr, so no `static_assert` test here.
27+
return 0;
28+
}

libcxx/test/support/test_container_comparisons.h

Lines changed: 124 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -81,4 +81,128 @@ constexpr bool test_ordered_container_spaceship() {
8181
return true;
8282
}
8383

84+
// Implementation detail of `test_ordered_map_container_spaceship`
85+
template <template <typename...> typename Container, typename Key, typename Val, typename Order>
86+
constexpr void test_ordered_map_container_spaceship_with_type() {
87+
// Empty containers
88+
{
89+
Container<Key, Val> l1;
90+
Container<Key, Val> l2;
91+
assert(testOrder(l1, l2, Order::equivalent));
92+
}
93+
// Identical contents
94+
{
95+
Container<Key, Val> l1{{1, 1}, {2, 1}};
96+
Container<Key, Val> l2{{1, 1}, {2, 1}};
97+
assert(testOrder(l1, l2, Order::equivalent));
98+
}
99+
// Less, due to contained values
100+
{
101+
Container<Key, Val> l1{{1, 1}, {2, 1}};
102+
Container<Key, Val> l2{{1, 1}, {2, 2}};
103+
assert(testOrder(l1, l2, Order::less));
104+
}
105+
// Greater, due to contained values
106+
{
107+
Container<Key, Val> l1{{1, 1}, {2, 3}};
108+
Container<Key, Val> l2{{1, 1}, {2, 2}};
109+
assert(testOrder(l1, l2, Order::greater));
110+
}
111+
// Shorter list
112+
{
113+
Container<Key, Val> l1{{1, 1}};
114+
Container<Key, Val> l2{{1, 1}, {2, 2}};
115+
assert(testOrder(l1, l2, Order::less));
116+
}
117+
// Longer list
118+
{
119+
Container<Key, Val> l1{{1, 2}, {2, 2}};
120+
Container<Key, Val> l2{{1, 1}};
121+
assert(testOrder(l1, l2, Order::greater));
122+
}
123+
// Unordered
124+
if constexpr (std::is_same_v<Val, PartialOrder>) {
125+
Container<Key, Val> l1{{1, 1}, {2, std::numeric_limits<int>::min()}};
126+
Container<Key, Val> l2{{1, 1}, {2, 2}};
127+
assert(testOrder(l1, l2, Order::unordered));
128+
}
129+
130+
// Identical contents
131+
{
132+
Container<Key, Val> l1{{1, 1}, {2, 1}, {2, 2}};
133+
Container<Key, Val> l2{{1, 1}, {2, 1}, {2, 2}};
134+
assert(testOrder(l1, l2, Order::equivalent));
135+
Container<Key, Val> l3{{1, 1}, {2, 1}, {2, 2}};
136+
Container<Key, Val> l4{{2, 1}, {2, 2}, {1, 1}};
137+
assert(testOrder(l3, l4, Order::equivalent));
138+
}
139+
// Less, due to contained values
140+
{
141+
Container<Key, Val> l1{{1, 1}, {2, 1}, {2, 1}};
142+
Container<Key, Val> l2{{1, 1}, {2, 2}, {2, 2}};
143+
assert(testOrder(l1, l2, Order::less));
144+
Container<Key, Val> l3{{1, 1}, {2, 1}, {2, 1}};
145+
Container<Key, Val> l4{{2, 2}, {2, 2}, {1, 1}};
146+
assert(testOrder(l3, l4, Order::less));
147+
}
148+
// Greater, due to contained values
149+
{
150+
Container<Key, Val> l1{{1, 1}, {2, 3}, {2, 3}};
151+
Container<Key, Val> l2{{1, 1}, {2, 2}, {2, 2}};
152+
assert(testOrder(l1, l2, Order::greater));
153+
Container<Key, Val> l3{{1, 1}, {2, 3}, {2, 3}};
154+
Container<Key, Val> l4{{2, 2}, {2, 2}, {1, 1}};
155+
assert(testOrder(l3, l4, Order::greater));
156+
}
157+
// Shorter list
158+
{
159+
Container<Key, Val> l1{{1, 1}, {2, 2}};
160+
Container<Key, Val> l2{{1, 1}, {2, 2}, {2, 2}, {3, 1}};
161+
assert(testOrder(l1, l2, Order::less));
162+
Container<Key, Val> l3{{1, 1}, {2, 2}};
163+
Container<Key, Val> l4{{3, 1}, {2, 2}, {2, 2}, {1, 1}};
164+
assert(testOrder(l3, l4, Order::less));
165+
}
166+
// Longer list
167+
{
168+
Container<Key, Val> l1{{1, 2}, {2, 2}, {2, 2}, {3, 1}};
169+
Container<Key, Val> l2{{1, 1}, {2, 2}};
170+
assert(testOrder(l1, l2, Order::greater));
171+
Container<Key, Val> l3{{1, 2}, {2, 2}, {2, 2}, {3, 1}};
172+
Container<Key, Val> l4{{2, 2}, {1, 1}};
173+
assert(testOrder(l3, l4, Order::greater));
174+
}
175+
// Unordered
176+
if constexpr (std::is_same_v<Val, PartialOrder>) {
177+
Container<Key, Val> l1{{1, 1}, {2, std::numeric_limits<int>::min()}, {2, 3}};
178+
Container<Key, Val> l2{{1, 1}, {2, 2}, {2, 3}};
179+
assert(testOrder(l1, l2, Order::unordered));
180+
Container<Key, Val> l3{{1, 1}, {2, std::numeric_limits<int>::min()}, {2, 3}};
181+
Container<Key, Val> l4{{2, 3}, {2, 2}, {1, 1}};
182+
assert(testOrder(l3, l4, Order::unordered));
183+
}
184+
}
185+
186+
// Tests the `operator<=>` on ordered containers
187+
template <template <typename...> typename Container>
188+
constexpr bool test_ordered_map_container_spaceship() {
189+
// The container should fulfil `std::three_way_comparable`
190+
static_assert(std::three_way_comparable<Container<int, int>>);
191+
192+
// Test different comparison categories
193+
test_ordered_map_container_spaceship_with_type<Container, int, int, std::strong_ordering>();
194+
test_ordered_map_container_spaceship_with_type<Container, int, StrongOrder, std::strong_ordering>();
195+
test_ordered_map_container_spaceship_with_type<Container, int, WeakOrder, std::weak_ordering>();
196+
test_ordered_map_container_spaceship_with_type<Container, int, PartialOrder, std::partial_ordering>();
197+
198+
// `LessAndEqComp` does not have `operator<=>`. ordering is sythesized based on `operator<`
199+
test_ordered_map_container_spaceship_with_type<Container, int, LessAndEqComp, std::weak_ordering>();
200+
201+
// Thanks to SFINAE, the following is not a compiler error but returns `false`
202+
struct NonComparable {};
203+
static_assert(!std::three_way_comparable<Container<int, NonComparable>>);
204+
205+
return true;
206+
}
207+
84208
#endif

0 commit comments

Comments
 (0)