[libcxx-commits] [libcxx] bc47a19 - [libc++][spaceship] Implement `operator<=>` for `map` and `multimap`

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Wed Mar 15 07:24:55 PDT 2023


Author: Hristo Hristov
Date: 2023-03-15T15:24:43+01:00
New Revision: bc47a195ccbb0b1a0296db9156a4bf3c06cf10a2

URL: https://github.com/llvm/llvm-project/commit/bc47a195ccbb0b1a0296db9156a4bf3c06cf10a2
DIFF: https://github.com/llvm/llvm-project/commit/bc47a195ccbb0b1a0296db9156a4bf3c06cf10a2.diff

LOG: [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

Added: 
    libcxx/test/libcxx/containers/associative/map/compare.three_way.pass.cpp
    libcxx/test/libcxx/containers/associative/multimap/compare.three_way.pass.cpp

Modified: 
    libcxx/docs/Status/SpaceshipProjects.csv
    libcxx/include/map
    libcxx/test/support/test_container_comparisons.h

Removed: 
    


################################################################################
diff  --git a/libcxx/docs/Status/SpaceshipProjects.csv b/libcxx/docs/Status/SpaceshipProjects.csv
index c043272071117..a3dc4ee81ec34 100644
--- a/libcxx/docs/Status/SpaceshipProjects.csv
+++ b/libcxx/docs/Status/SpaceshipProjects.csv
@@ -40,8 +40,8 @@ Section,Description,Dependencies,Assignee,Complete
 | `[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|
 | `[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|
 | `[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|
-| `[associative.map.syn] <https://wg21.link/associative.map.syn>`_ (`general <https://wg21.link/container.opt.reqmts>`_),"| map
-| multimap",[expos.only.func],Unassigned,|Not Started|
+| `[associative.map.syn] <https://wg21.link/associative.map.syn>`_ (`general <https://wg21.link/container.opt.reqmts>`_),"| `map <https://reviews.llvm.org/D145976>`_
+| `multimap <https://reviews.llvm.org/D145976>`_",[expos.only.func],Hristo Hristov,|Complete|
 | `[associative.set.syn] <https://wg21.link/associative.set.syn>`_ (`general <https://wg21.link/container.opt.reqmts>`_),"| multiset
 | set",[expos.only.func],Unassigned,|Not Started|
 | `[queue.ops] <https://wg21.link/queue.ops>`_,| queue,None,Unassigned,|Not Started|

diff  --git a/libcxx/include/map b/libcxx/include/map
index 838c96cea8440..c190e3fc069b7 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -250,27 +250,32 @@ operator==(const map<Key, T, Compare, Allocator>& x,
 template <class Key, class T, class Compare, class Allocator>
 bool
 operator< (const map<Key, T, Compare, Allocator>& x,
-           const map<Key, T, Compare, Allocator>& y);
+           const map<Key, T, Compare, Allocator>& y);      // removed in C++20
 
 template <class Key, class T, class Compare, class Allocator>
 bool
 operator!=(const map<Key, T, Compare, Allocator>& x,
-           const map<Key, T, Compare, Allocator>& y);
+           const map<Key, T, Compare, Allocator>& y);      // removed in C++20
 
 template <class Key, class T, class Compare, class Allocator>
 bool
 operator> (const map<Key, T, Compare, Allocator>& x,
-           const map<Key, T, Compare, Allocator>& y);
+           const map<Key, T, Compare, Allocator>& y);      // removed in C++20
 
 template <class Key, class T, class Compare, class Allocator>
 bool
 operator>=(const map<Key, T, Compare, Allocator>& x,
-           const map<Key, T, Compare, Allocator>& y);
+           const map<Key, T, Compare, Allocator>& y);      // removed in C++20
 
 template <class Key, class T, class Compare, class Allocator>
 bool
 operator<=(const map<Key, T, Compare, Allocator>& x,
-           const map<Key, T, Compare, Allocator>& y);
+           const map<Key, T, Compare, Allocator>& y);      // removed in C++20
+
+template<class Key, class T, class Compare, class Allocator>
+  synth-three-way-result<pair<const Key, T>>
+    operator<=>(const map<Key, T, Compare, Allocator>& x,
+                const map<Key, T, Compare, Allocator>& y); // since C++20
 
 // specialized algorithms:
 template <class Key, class T, class Compare, class Allocator>
@@ -491,27 +496,32 @@ operator==(const multimap<Key, T, Compare, Allocator>& x,
 template <class Key, class T, class Compare, class Allocator>
 bool
 operator< (const multimap<Key, T, Compare, Allocator>& x,
-           const multimap<Key, T, Compare, Allocator>& y);
+           const multimap<Key, T, Compare, Allocator>& y);      // removed in C++20
 
 template <class Key, class T, class Compare, class Allocator>
 bool
 operator!=(const multimap<Key, T, Compare, Allocator>& x,
-           const multimap<Key, T, Compare, Allocator>& y);
+           const multimap<Key, T, Compare, Allocator>& y);      // removed in C++20
 
 template <class Key, class T, class Compare, class Allocator>
 bool
 operator> (const multimap<Key, T, Compare, Allocator>& x,
-           const multimap<Key, T, Compare, Allocator>& y);
+           const multimap<Key, T, Compare, Allocator>& y);      // removed in C++20
 
 template <class Key, class T, class Compare, class Allocator>
 bool
 operator>=(const multimap<Key, T, Compare, Allocator>& x,
-           const multimap<Key, T, Compare, Allocator>& y);
+           const multimap<Key, T, Compare, Allocator>& y);      // removed in C++20
 
 template <class Key, class T, class Compare, class Allocator>
 bool
 operator<=(const multimap<Key, T, Compare, Allocator>& x,
-           const multimap<Key, T, Compare, Allocator>& y);
+           const multimap<Key, T, Compare, Allocator>& y);      // removed in C++20
+
+template<class Key, class T, class Compare, class Allocator>
+  synth-three-way-result<pair<const Key, T>>
+    operator<=>(const multimap<Key, T, Compare, Allocator>& x,
+                const multimap<Key, T, Compare, Allocator>& y); // since c++20
 
 // specialized algorithms:
 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
 
 #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/binary_function.h>
@@ -1676,6 +1687,8 @@ operator==(const map<_Key, _Tp, _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 _Tp, class _Compare, class _Allocator>
 inline _LIBCPP_INLINE_VISIBILITY
 bool
@@ -1721,6 +1734,20 @@ operator<=(const map<_Key, _Tp, _Compare, _Allocator>& __x,
     return !(__y < __x);
 }
 
+#else // #if _LIBCPP_STD_VER <= 17
+
+template<class _Key, class _Tp, class _Compare, class _Allocator>
+inline _LIBCPP_HIDE_FROM_ABI
+__synth_three_way_result<pair<const _Key, _Tp>>
+operator<=>(const map<_Key, _Tp, _Compare, _Allocator>& __x,
+            const map<_Key, _Tp, _Compare, _Allocator>& __y)
+{
+    return std::lexicographical_compare_three_way(
+        __x.begin(), __x.end(), __y.begin(), __y.end(), __synth_three_way);
+}
+
+#endif // #if _LIBCPP_STD_VER <= 17
+
 template <class _Key, class _Tp, class _Compare, class _Allocator>
 inline _LIBCPP_INLINE_VISIBILITY
 void
@@ -2270,6 +2297,8 @@ operator==(const multimap<_Key, _Tp, _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 _Tp, class _Compare, class _Allocator>
 inline _LIBCPP_INLINE_VISIBILITY
 bool
@@ -2315,6 +2344,20 @@ operator<=(const multimap<_Key, _Tp, _Compare, _Allocator>& __x,
     return !(__y < __x);
 }
 
+#else // #if _LIBCPP_STD_VER <= 17
+
+template<class _Key, class _Tp, class _Compare, class _Allocator>
+inline _LIBCPP_HIDE_FROM_ABI
+__synth_three_way_result<pair<const _Key, _Tp>>
+operator<=>(const multimap<_Key, _Tp, _Compare, _Allocator>& __x,
+            const multimap<_Key, _Tp, _Compare, _Allocator>& __y)
+{
+    return std::lexicographical_compare_three_way(
+        __x.begin(), __x.end(), __y.begin(), __y.end(), __synth_three_way);
+}
+
+#endif // #if _LIBCPP_STD_VER <= 17
+
 template <class _Key, class _Tp, class _Compare, class _Allocator>
 inline _LIBCPP_INLINE_VISIBILITY
 void

diff  --git a/libcxx/test/libcxx/containers/associative/map/compare.three_way.pass.cpp b/libcxx/test/libcxx/containers/associative/map/compare.three_way.pass.cpp
new file mode 100644
index 0000000000000..0e1c57f123e58
--- /dev/null
+++ b/libcxx/test/libcxx/containers/associative/map/compare.three_way.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <map>
+
+// class map
+
+// template<class Key, class T, class Compare, class Allocator>
+//   synth-three-way-result<pair<const Key, T>>
+//     operator<=>(const map<Key, T, Compare, Allocator>& x,
+//                 const map<Key, T, Compare, Allocator>& y);
+
+#include <cassert>
+#include <map>
+
+#include "test_container_comparisons.h"
+
+int main(int, char**) {
+  assert(test_ordered_map_container_spaceship<std::map>());
+  // `std::map` is not constexpr, so no `static_assert` test here.
+  return 0;
+}

diff  --git a/libcxx/test/libcxx/containers/associative/multimap/compare.three_way.pass.cpp b/libcxx/test/libcxx/containers/associative/multimap/compare.three_way.pass.cpp
new file mode 100644
index 0000000000000..c7febc362cd63
--- /dev/null
+++ b/libcxx/test/libcxx/containers/associative/multimap/compare.three_way.pass.cpp
@@ -0,0 +1,28 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+
+// <map>
+
+// class multimap
+
+// template<class Key, class T, class Compare, class Allocator>
+//   synth-three-way-result<pair<const Key, T>>
+//     operator<=>(const multimap<Key, T, Compare, Allocator>& x,
+//                 const multimap<Key, T, Compare, Allocator>& y);
+
+#include <cassert>
+#include <map>
+
+#include "test_container_comparisons.h"
+
+int main(int, char**) {
+  assert(test_ordered_map_container_spaceship<std::multimap>());
+  // `std::multimap` is not constexpr, so no `static_assert` test here.
+  return 0;
+}

diff  --git a/libcxx/test/support/test_container_comparisons.h b/libcxx/test/support/test_container_comparisons.h
index d4659bd1aa5d1..8687d9ae80d53 100644
--- a/libcxx/test/support/test_container_comparisons.h
+++ b/libcxx/test/support/test_container_comparisons.h
@@ -81,4 +81,128 @@ constexpr bool test_ordered_container_spaceship() {
   return true;
 }
 
+// Implementation detail of `test_ordered_map_container_spaceship`
+template <template <typename...> typename Container, typename Key, typename Val, typename Order>
+constexpr void test_ordered_map_container_spaceship_with_type() {
+  // Empty containers
+  {
+    Container<Key, Val> l1;
+    Container<Key, Val> l2;
+    assert(testOrder(l1, l2, Order::equivalent));
+  }
+  // Identical contents
+  {
+    Container<Key, Val> l1{{1, 1}, {2, 1}};
+    Container<Key, Val> l2{{1, 1}, {2, 1}};
+    assert(testOrder(l1, l2, Order::equivalent));
+  }
+  // Less, due to contained values
+  {
+    Container<Key, Val> l1{{1, 1}, {2, 1}};
+    Container<Key, Val> l2{{1, 1}, {2, 2}};
+    assert(testOrder(l1, l2, Order::less));
+  }
+  // Greater, due to contained values
+  {
+    Container<Key, Val> l1{{1, 1}, {2, 3}};
+    Container<Key, Val> l2{{1, 1}, {2, 2}};
+    assert(testOrder(l1, l2, Order::greater));
+  }
+  // Shorter list
+  {
+    Container<Key, Val> l1{{1, 1}};
+    Container<Key, Val> l2{{1, 1}, {2, 2}};
+    assert(testOrder(l1, l2, Order::less));
+  }
+  // Longer list
+  {
+    Container<Key, Val> l1{{1, 2}, {2, 2}};
+    Container<Key, Val> l2{{1, 1}};
+    assert(testOrder(l1, l2, Order::greater));
+  }
+  // Unordered
+  if constexpr (std::is_same_v<Val, PartialOrder>) {
+    Container<Key, Val> l1{{1, 1}, {2, std::numeric_limits<int>::min()}};
+    Container<Key, Val> l2{{1, 1}, {2, 2}};
+    assert(testOrder(l1, l2, Order::unordered));
+  }
+
+  // Identical contents
+  {
+    Container<Key, Val> l1{{1, 1}, {2, 1}, {2, 2}};
+    Container<Key, Val> l2{{1, 1}, {2, 1}, {2, 2}};
+    assert(testOrder(l1, l2, Order::equivalent));
+    Container<Key, Val> l3{{1, 1}, {2, 1}, {2, 2}};
+    Container<Key, Val> l4{{2, 1}, {2, 2}, {1, 1}};
+    assert(testOrder(l3, l4, Order::equivalent));
+  }
+  // Less, due to contained values
+  {
+    Container<Key, Val> l1{{1, 1}, {2, 1}, {2, 1}};
+    Container<Key, Val> l2{{1, 1}, {2, 2}, {2, 2}};
+    assert(testOrder(l1, l2, Order::less));
+    Container<Key, Val> l3{{1, 1}, {2, 1}, {2, 1}};
+    Container<Key, Val> l4{{2, 2}, {2, 2}, {1, 1}};
+    assert(testOrder(l3, l4, Order::less));
+  }
+  // Greater, due to contained values
+  {
+    Container<Key, Val> l1{{1, 1}, {2, 3}, {2, 3}};
+    Container<Key, Val> l2{{1, 1}, {2, 2}, {2, 2}};
+    assert(testOrder(l1, l2, Order::greater));
+    Container<Key, Val> l3{{1, 1}, {2, 3}, {2, 3}};
+    Container<Key, Val> l4{{2, 2}, {2, 2}, {1, 1}};
+    assert(testOrder(l3, l4, Order::greater));
+  }
+  // Shorter list
+  {
+    Container<Key, Val> l1{{1, 1}, {2, 2}};
+    Container<Key, Val> l2{{1, 1}, {2, 2}, {2, 2}, {3, 1}};
+    assert(testOrder(l1, l2, Order::less));
+    Container<Key, Val> l3{{1, 1}, {2, 2}};
+    Container<Key, Val> l4{{3, 1}, {2, 2}, {2, 2}, {1, 1}};
+    assert(testOrder(l3, l4, Order::less));
+  }
+  // Longer list
+  {
+    Container<Key, Val> l1{{1, 2}, {2, 2}, {2, 2}, {3, 1}};
+    Container<Key, Val> l2{{1, 1}, {2, 2}};
+    assert(testOrder(l1, l2, Order::greater));
+    Container<Key, Val> l3{{1, 2}, {2, 2}, {2, 2}, {3, 1}};
+    Container<Key, Val> l4{{2, 2}, {1, 1}};
+    assert(testOrder(l3, l4, Order::greater));
+  }
+  // Unordered
+  if constexpr (std::is_same_v<Val, PartialOrder>) {
+    Container<Key, Val> l1{{1, 1}, {2, std::numeric_limits<int>::min()}, {2, 3}};
+    Container<Key, Val> l2{{1, 1}, {2, 2}, {2, 3}};
+    assert(testOrder(l1, l2, Order::unordered));
+    Container<Key, Val> l3{{1, 1}, {2, std::numeric_limits<int>::min()}, {2, 3}};
+    Container<Key, Val> l4{{2, 3}, {2, 2}, {1, 1}};
+    assert(testOrder(l3, l4, Order::unordered));
+  }
+}
+
+// Tests the `operator<=>` on ordered containers
+template <template <typename...> typename Container>
+constexpr bool test_ordered_map_container_spaceship() {
+  // The container should fulfil `std::three_way_comparable`
+  static_assert(std::three_way_comparable<Container<int, int>>);
+
+  // Test 
diff erent comparison categories
+  test_ordered_map_container_spaceship_with_type<Container, int, int, std::strong_ordering>();
+  test_ordered_map_container_spaceship_with_type<Container, int, StrongOrder, std::strong_ordering>();
+  test_ordered_map_container_spaceship_with_type<Container, int, WeakOrder, std::weak_ordering>();
+  test_ordered_map_container_spaceship_with_type<Container, int, PartialOrder, std::partial_ordering>();
+
+  // `LessAndEqComp` does not have `operator<=>`. ordering is sythesized based on `operator<`
+  test_ordered_map_container_spaceship_with_type<Container, int, LessAndEqComp, std::weak_ordering>();
+
+  // Thanks to SFINAE, the following is not a compiler error but returns `false`
+  struct NonComparable {};
+  static_assert(!std::three_way_comparable<Container<int, NonComparable>>);
+
+  return true;
+}
+
 #endif


        


More information about the libcxx-commits mailing list