[libcxx-commits] [libcxx] [libc++] Avoid double comparisons when uniquing flat_set/flat_map (PR #214118)

Adib Pathan via libcxx-commits libcxx-commits at lists.llvm.org
Tue Aug 4 20:50:47 PDT 2026


https://github.com/adxb-pxthxn updated https://github.com/llvm/llvm-project/pull/214118

>From e0453593d301cb640542bb4650b9dd89daf9d41f Mon Sep 17 00:00:00 2001
From: Adib <adibpathan280 at gmail.com>
Date: Wed, 5 Aug 2026 11:57:15 +0930
Subject: [PATCH 1/4] [libc++] Avoid double comparisons when uniquing
 flat_set/flat_map

after sorting, duplicates can be detected with a one direction
predicate. Use a flat local helper instead of ranges::unique so we
do not rely on an equivalence relation.

Fixes #175086
---
 libcxx/include/__flat_map/flat_map.h | 30 ++++++++++++++++++++++------
 libcxx/include/__flat_set/flat_set.h | 30 ++++++++++++++++++++++------
 2 files changed, 48 insertions(+), 12 deletions(-)

diff --git a/libcxx/include/__flat_map/flat_map.h b/libcxx/include/__flat_map/flat_map.h
index 50487cada24c0..d1ff3b7faec1d 100644
--- a/libcxx/include/__flat_map/flat_map.h
+++ b/libcxx/include/__flat_map/flat_map.h
@@ -10,6 +10,7 @@
 #ifndef _LIBCPP___FLAT_MAP_FLAT_MAP_H
 #define _LIBCPP___FLAT_MAP_FLAT_MAP_H
 
+#include <__algorithm/iterator_operations.h>
 #include <__algorithm/lexicographical_compare_three_way.h>
 #include <__algorithm/lower_bound.h>
 #include <__algorithm/min.h>
@@ -17,7 +18,6 @@
 #include <__algorithm/ranges_equal.h>
 #include <__algorithm/ranges_inplace_merge.h>
 #include <__algorithm/ranges_sort.h>
-#include <__algorithm/ranges_unique.h>
 #include <__algorithm/remove_if.h>
 #include <__algorithm/upper_bound.h>
 #include <__assert>
@@ -938,13 +938,30 @@ class flat_map {
     return ranges::adjacent_find(__key_container, __greater_or_equal_to) == ranges::end(__key_container);
   }
 
+  template <class _Iter, class _Sent, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static _Iter
+  __deduplicate_sorted(_Iter __first, _Sent __last, _Predicate __pred) {
+    __first = ranges::adjacent_find(__first, __last, __pred);
+    if (__first == __last) {
+      return __first;
+    }
+
+    _Iter __i = __first;
+    while (++__i != __last) {
+      if (!__pred(*__first, *__i)) {
+        *++__first = _IterOps<_RangeAlgPolicy>::__iter_move(__i);
+      }
+    }
+    return ++__first;
+  }
+
   // This function is only used in constructors. So there is not exception handling in this function.
   // If the function exits via an exception, there will be no flat_map object constructed, thus, there
   // is no invariant state to preserve
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __sort_and_unique() {
     auto __zv = ranges::views::zip(__containers_.keys, __containers_.values);
     ranges::sort(__zv, __compare_, [](const auto& __p) -> decltype(auto) { return std::get<0>(__p); });
-    auto __dup_start = ranges::unique(__zv, __key_equiv(__compare_)).begin();
+    auto __dup_start = __deduplicate_sorted(__zv.begin(), __zv.end(), __key_not_less(__compare_));
     auto __dist      = ranges::distance(__zv.begin(), __dup_start);
     __containers_.keys.erase(__containers_.keys.begin() + __dist, __containers_.keys.end());
     __containers_.values.erase(__containers_.values.begin() + __dist, __containers_.values.end());
@@ -979,7 +996,7 @@ class flat_map {
       }
       ranges::inplace_merge(__zv.begin(), __zv.begin() + __append_start_offset, __end, __compare_key);
 
-      auto __dup_start = ranges::unique(__zv, __key_equiv(__compare_)).begin();
+      auto __dup_start = __deduplicate_sorted(__zv.begin(), __zv.end(), __key_not_less(__compare_));
       auto __dist      = ranges::distance(__zv.begin(), __dup_start);
       __containers_.keys.erase(__containers_.keys.begin() + __dist, __containers_.keys.end());
       __containers_.values.erase(__containers_.values.begin() + __dist, __containers_.values.end());
@@ -1135,11 +1152,12 @@ class flat_map {
   containers __containers_;
   _LIBCPP_NO_UNIQUE_ADDRESS key_compare __compare_;
 
-  struct __key_equiv {
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_equiv(key_compare __c) : __comp_(__c) {}
+  struct __key_not_less {
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_not_less(key_compare __c) : __comp_(__c) {}
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
     operator()(const_reference __x, const_reference __y) const {
-      return !__comp_(std::get<0>(__x), std::get<0>(__y)) && !__comp_(std::get<0>(__y), std::get<0>(__x));
+      // __x is always before __y in sorted ranges.
+      return !__comp_(std::get<0>(__x), std::get<0>(__y));
     }
     key_compare __comp_;
   };
diff --git a/libcxx/include/__flat_set/flat_set.h b/libcxx/include/__flat_set/flat_set.h
index 57c3926e33b68..176a8516c45fd 100644
--- a/libcxx/include/__flat_set/flat_set.h
+++ b/libcxx/include/__flat_set/flat_set.h
@@ -10,13 +10,13 @@
 #ifndef _LIBCPP___FLAT_SET_FLAT_SET_H
 #define _LIBCPP___FLAT_SET_FLAT_SET_H
 
+#include <__algorithm/iterator_operations.h>
 #include <__algorithm/lexicographical_compare_three_way.h>
 #include <__algorithm/lower_bound.h>
 #include <__algorithm/ranges_adjacent_find.h>
 #include <__algorithm/ranges_equal.h>
 #include <__algorithm/ranges_inplace_merge.h>
 #include <__algorithm/ranges_sort.h>
-#include <__algorithm/ranges_unique.h>
 #include <__algorithm/remove_if.h>
 #include <__algorithm/upper_bound.h>
 #include <__assert>
@@ -683,12 +683,29 @@ class flat_set {
     return ranges::adjacent_find(__key_container, __greater_or_equal_to) == ranges::end(__key_container);
   }
 
+  template <class _Iter, class _Sent, class _Predicate>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static _Iter
+  __deduplicate_sorted(_Iter __first, _Sent __last, _Predicate __pred) {
+    __first = ranges::adjacent_find(__first, __last, __pred);
+    if (__first == __last) {
+      return __first;
+    }
+
+    _Iter __i = __first;
+    while (++__i != __last) {
+      if (!__pred(*__first, *__i)) {
+        *++__first = _IterOps<_RangeAlgPolicy>::__iter_move(__i);
+      }
+    }
+    return ++__first;
+  }
+
   // This function is only used in constructors. So there is not exception handling in this function.
   // If the function exits via an exception, there will be no flat_set object constructed, thus, there
   // is no invariant state to preserve
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __sort_and_unique() {
     ranges::sort(__keys_, __compare_);
-    auto __dup_start = ranges::unique(__keys_, __key_equiv(__compare_)).begin();
+    auto __dup_start = __deduplicate_sorted(__keys_.begin(), __keys_.end(), __key_not_less(__compare_));
     __keys_.erase(__dup_start, __keys_.end());
   }
 
@@ -706,7 +723,7 @@ class flat_set {
       }
       ranges::inplace_merge(__keys_.begin(), __keys_.begin() + __old_size, __keys_.end(), __compare_);
 
-      auto __dup_start = ranges::unique(__keys_, __key_equiv(__compare_)).begin();
+      auto __dup_start = __deduplicate_sorted(__keys_.begin(), __keys_.end(), __key_not_less(__compare_));
       __keys_.erase(__dup_start, __keys_.end());
     }
     __on_failure.__complete();
@@ -781,11 +798,12 @@ class flat_set {
   _KeyContainer __keys_;
   _LIBCPP_NO_UNIQUE_ADDRESS key_compare __compare_;
 
-  struct __key_equiv {
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_equiv(key_compare __c) : __comp_(__c) {}
+  struct __key_not_less {
+    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_not_less(key_compare __c) : __comp_(__c) {}
     _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
     operator()(const_reference __x, const_reference __y) const {
-      return !__comp_(__x, __y) && !__comp_(__y, __x);
+      // __x is always before __y in sorted ranges.
+      return !__comp_(__x, __y);
     }
     key_compare __comp_;
   };

>From e84b52dd13b3caeeca0bbb239946c726e07cccc0 Mon Sep 17 00:00:00 2001
From: Adib <adibpathan280 at gmail.com>
Date: Wed, 5 Aug 2026 11:57:41 +0930
Subject: [PATCH 2/4] [libc++] test flat_set/flat_map uniquing uses
 one-direction comparisons

---
 .../flat.map.cons/initializer_list.pass.cpp   | 20 +++++++++++++++++++
 .../flat.set.cons/initializer_list.pass.cpp   | 19 ++++++++++++++++++
 2 files changed, 39 insertions(+)

diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/initializer_list.pass.cpp
index ef49a6ee9f986..53e9210f32153 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/initializer_list.pass.cpp
@@ -35,6 +35,25 @@ struct DefaultCtableComp {
   bool default_constructed_ = false;
 };
 
+struct CountingComp {
+  constexpr explicit CountingComp(int& count) : count_(count) {}
+
+  constexpr bool operator()(int x, int y) const {
+    ++count_;
+    return x < y;
+  }
+
+  int& count_;
+};
+
+void test_dedup_uses_one_direction_comparisons() {
+  int count = 0;
+  std::flat_map<int, int, CountingComp> map({{0, 1}, {0, 2}}, CountingComp(count));
+  assert(map.size() == 1);
+  assert(map.begin()->first == 0);
+  assert(count == 2);
+}
+
 template <template <class...> class KeyContainer, template <class...> class ValueContainer>
 constexpr void test() {
   std::pair<int, long> expected[] = {{1, 1}, {2, 2}, {3, 3}, {5, 2}};
@@ -168,6 +187,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
+  test_dedup_uses_one_direction_comparisons();
 #if TEST_STD_VER >= 26
   static_assert(test());
 #endif
diff --git a/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/initializer_list.pass.cpp
index 2a8e4c40ebe2b..ebf9781a2bc58 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/initializer_list.pass.cpp
@@ -35,6 +35,24 @@ struct DefaultCtableComp {
   bool default_constructed_ = false;
 };
 
+struct CountingComp {
+  constexpr explicit CountingComp(int& count) : count_(count) {}
+
+  constexpr bool operator()(int x, int y) const {
+    ++count_;
+    return x < y;
+  }
+
+  int& count_;
+};
+
+void test_dedup_uses_one_direction_comparisons() {
+  int count = 0;
+  std::flat_set<int, CountingComp> set({0, 0}, CountingComp(count));
+  assert(set.size() == 1);
+  assert(count == 2);
+}
+
 template <template <class...> class KeyContainer>
 constexpr void test() {
   int expected[] = {1, 2, 3, 5};
@@ -160,6 +178,7 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
+  test_dedup_uses_one_direction_comparisons();
 #if TEST_STD_VER >= 26
   static_assert(test());
 #endif

>From 37a06b241f716e165e2880f2282a7dcf3b0b31b6 Mon Sep 17 00:00:00 2001
From: Adib <adibpathan280 at gmail.com>
Date: Wed, 5 Aug 2026 12:10:09 +0930
Subject: [PATCH 3/4] [libc++] Fix extra predicate call in flat_* dedup helper

---
 libcxx/include/__flat_map/flat_map.h | 2 +-
 libcxx/include/__flat_set/flat_set.h | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/libcxx/include/__flat_map/flat_map.h b/libcxx/include/__flat_map/flat_map.h
index d1ff3b7faec1d..98510ee102972 100644
--- a/libcxx/include/__flat_map/flat_map.h
+++ b/libcxx/include/__flat_map/flat_map.h
@@ -947,7 +947,7 @@ class flat_map {
     }
 
     _Iter __i = __first;
-    while (++__i != __last) {
+    for (++__i; ++__i != __last;) {
       if (!__pred(*__first, *__i)) {
         *++__first = _IterOps<_RangeAlgPolicy>::__iter_move(__i);
       }
diff --git a/libcxx/include/__flat_set/flat_set.h b/libcxx/include/__flat_set/flat_set.h
index 176a8516c45fd..c8d7dc810b748 100644
--- a/libcxx/include/__flat_set/flat_set.h
+++ b/libcxx/include/__flat_set/flat_set.h
@@ -692,7 +692,7 @@ class flat_set {
     }
 
     _Iter __i = __first;
-    while (++__i != __last) {
+    for (++__i; ++__i != __last;) {
       if (!__pred(*__first, *__i)) {
         *++__first = _IterOps<_RangeAlgPolicy>::__iter_move(__i);
       }

>From 1579791a9b48ca7c219e4702d1355c3d42fa13aa Mon Sep 17 00:00:00 2001
From: Adib <adibpathan280 at gmail.com>
Date: Wed, 5 Aug 2026 13:20:18 +0930
Subject: [PATCH 4/4] [libc++] regarding frederick-vs-ja's feedback, flat_*
 dedup helper and libc++-specific tests

---
 libcxx/include/__flat_map/flat_map.h          | 24 ++++------
 libcxx/include/__flat_set/flat_set.h          | 24 ++++------
 .../flat.map/dedup.comparison_count.pass.cpp  | 45 +++++++++++++++++++
 .../flat.set/dedup.comparison_count.pass.cpp  | 45 +++++++++++++++++++
 .../flat.map.cons/initializer_list.pass.cpp   | 20 ---------
 .../flat.set.cons/initializer_list.pass.cpp   | 19 --------
 6 files changed, 106 insertions(+), 71 deletions(-)
 create mode 100644 libcxx/test/libcxx/containers/container.adaptors/flat.map/dedup.comparison_count.pass.cpp
 create mode 100644 libcxx/test/libcxx/containers/container.adaptors/flat.set/dedup.comparison_count.pass.cpp

diff --git a/libcxx/include/__flat_map/flat_map.h b/libcxx/include/__flat_map/flat_map.h
index 98510ee102972..18748d617bb73 100644
--- a/libcxx/include/__flat_map/flat_map.h
+++ b/libcxx/include/__flat_map/flat_map.h
@@ -938,17 +938,18 @@ class flat_map {
     return ranges::adjacent_find(__key_container, __greater_or_equal_to) == ranges::end(__key_container);
   }
 
-  template <class _Iter, class _Sent, class _Predicate>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static _Iter
-  __deduplicate_sorted(_Iter __first, _Sent __last, _Predicate __pred) {
-    __first = ranges::adjacent_find(__first, __last, __pred);
+  template <class _Iter, class _Sent>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Iter __deduplicate_sorted(_Iter __first, _Sent __last) {
+    __first = ranges::adjacent_find(__first, __last, [this](const auto& __x, const auto& __y) -> bool {
+      return !__compare_(std::get<0>(__x), std::get<0>(__y));
+    });
     if (__first == __last) {
       return __first;
     }
 
     _Iter __i = __first;
     for (++__i; ++__i != __last;) {
-      if (!__pred(*__first, *__i)) {
+      if (!__compare_(std::get<0>(*__first), std::get<0>(*__i))) {
         *++__first = _IterOps<_RangeAlgPolicy>::__iter_move(__i);
       }
     }
@@ -961,7 +962,7 @@ class flat_map {
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __sort_and_unique() {
     auto __zv = ranges::views::zip(__containers_.keys, __containers_.values);
     ranges::sort(__zv, __compare_, [](const auto& __p) -> decltype(auto) { return std::get<0>(__p); });
-    auto __dup_start = __deduplicate_sorted(__zv.begin(), __zv.end(), __key_not_less(__compare_));
+    auto __dup_start = __deduplicate_sorted(__zv.begin(), __zv.end());
     auto __dist      = ranges::distance(__zv.begin(), __dup_start);
     __containers_.keys.erase(__containers_.keys.begin() + __dist, __containers_.keys.end());
     __containers_.values.erase(__containers_.values.begin() + __dist, __containers_.values.end());
@@ -996,7 +997,7 @@ class flat_map {
       }
       ranges::inplace_merge(__zv.begin(), __zv.begin() + __append_start_offset, __end, __compare_key);
 
-      auto __dup_start = __deduplicate_sorted(__zv.begin(), __zv.end(), __key_not_less(__compare_));
+      auto __dup_start = __deduplicate_sorted(__zv.begin(), __zv.end());
       auto __dist      = ranges::distance(__zv.begin(), __dup_start);
       __containers_.keys.erase(__containers_.keys.begin() + __dist, __containers_.keys.end());
       __containers_.values.erase(__containers_.values.begin() + __dist, __containers_.values.end());
@@ -1152,15 +1153,6 @@ class flat_map {
   containers __containers_;
   _LIBCPP_NO_UNIQUE_ADDRESS key_compare __compare_;
 
-  struct __key_not_less {
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_not_less(key_compare __c) : __comp_(__c) {}
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
-    operator()(const_reference __x, const_reference __y) const {
-      // __x is always before __y in sorted ranges.
-      return !__comp_(std::get<0>(__x), std::get<0>(__y));
-    }
-    key_compare __comp_;
-  };
 };
 
 template <class _KeyContainer, class _MappedContainer, class _Compare = less<typename _KeyContainer::value_type>>
diff --git a/libcxx/include/__flat_set/flat_set.h b/libcxx/include/__flat_set/flat_set.h
index c8d7dc810b748..30be8dab6ab59 100644
--- a/libcxx/include/__flat_set/flat_set.h
+++ b/libcxx/include/__flat_set/flat_set.h
@@ -683,17 +683,18 @@ class flat_set {
     return ranges::adjacent_find(__key_container, __greater_or_equal_to) == ranges::end(__key_container);
   }
 
-  template <class _Iter, class _Sent, class _Predicate>
-  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 static _Iter
-  __deduplicate_sorted(_Iter __first, _Sent __last, _Predicate __pred) {
-    __first = ranges::adjacent_find(__first, __last, __pred);
+  template <class _Iter, class _Sent>
+  _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _Iter __deduplicate_sorted(_Iter __first, _Sent __last) {
+    __first = ranges::adjacent_find(__first, __last, [this](const auto& __x, const auto& __y) -> bool {
+      return !__compare_(__x, __y);
+    });
     if (__first == __last) {
       return __first;
     }
 
     _Iter __i = __first;
     for (++__i; ++__i != __last;) {
-      if (!__pred(*__first, *__i)) {
+      if (!__compare_(*__first, *__i)) {
         *++__first = _IterOps<_RangeAlgPolicy>::__iter_move(__i);
       }
     }
@@ -705,7 +706,7 @@ class flat_set {
   // is no invariant state to preserve
   _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void __sort_and_unique() {
     ranges::sort(__keys_, __compare_);
-    auto __dup_start = __deduplicate_sorted(__keys_.begin(), __keys_.end(), __key_not_less(__compare_));
+    auto __dup_start = __deduplicate_sorted(__keys_.begin(), __keys_.end());
     __keys_.erase(__dup_start, __keys_.end());
   }
 
@@ -723,7 +724,7 @@ class flat_set {
       }
       ranges::inplace_merge(__keys_.begin(), __keys_.begin() + __old_size, __keys_.end(), __compare_);
 
-      auto __dup_start = __deduplicate_sorted(__keys_.begin(), __keys_.end(), __key_not_less(__compare_));
+      auto __dup_start = __deduplicate_sorted(__keys_.begin(), __keys_.end());
       __keys_.erase(__dup_start, __keys_.end());
     }
     __on_failure.__complete();
@@ -798,15 +799,6 @@ class flat_set {
   _KeyContainer __keys_;
   _LIBCPP_NO_UNIQUE_ADDRESS key_compare __compare_;
 
-  struct __key_not_less {
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 __key_not_less(key_compare __c) : __comp_(__c) {}
-    _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 bool
-    operator()(const_reference __x, const_reference __y) const {
-      // __x is always before __y in sorted ranges.
-      return !__comp_(__x, __y);
-    }
-    key_compare __comp_;
-  };
 };
 
 template <class _KeyContainer, class _Compare = less<typename _KeyContainer::value_type>>
diff --git a/libcxx/test/libcxx/containers/container.adaptors/flat.map/dedup.comparison_count.pass.cpp b/libcxx/test/libcxx/containers/container.adaptors/flat.map/dedup.comparison_count.pass.cpp
new file mode 100644
index 0000000000000..01d341efff90e
--- /dev/null
+++ b/libcxx/test/libcxx/containers/container.adaptors/flat.map/dedup.comparison_count.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <flat_map>
+
+// libc++ extension: sorted deduplication uses one-direction key comparison.
+
+#include <flat_map>
+
+#include <cassert>
+
+#include "test_macros.h"
+
+struct CountingComp {
+  constexpr explicit CountingComp(int& count) : count_(count) {}
+
+  constexpr bool operator()(int x, int y) const {
+    ++count_;
+    return x < y;
+  }
+
+  int& count_;
+};
+
+constexpr bool test() {
+  int count = 0;
+  std::flat_map<int, int, CountingComp> map({{0, 1}, {0, 2}}, CountingComp(count));
+  return map.size() == 1 && map.begin()->first == 0 && count == 2;
+}
+
+int main(int, char**) {
+  assert(test());
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/libcxx/containers/container.adaptors/flat.set/dedup.comparison_count.pass.cpp b/libcxx/test/libcxx/containers/container.adaptors/flat.set/dedup.comparison_count.pass.cpp
new file mode 100644
index 0000000000000..65688eb4c4276
--- /dev/null
+++ b/libcxx/test/libcxx/containers/container.adaptors/flat.set/dedup.comparison_count.pass.cpp
@@ -0,0 +1,45 @@
+//===----------------------------------------------------------------------===//
+//
+// 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, c++20
+
+// <flat_set>
+
+// libc++ extension: sorted deduplication uses one-direction key comparison.
+
+#include <flat_set>
+
+#include <cassert>
+
+#include "test_macros.h"
+
+struct CountingComp {
+  constexpr explicit CountingComp(int& count) : count_(count) {}
+
+  constexpr bool operator()(int x, int y) const {
+    ++count_;
+    return x < y;
+  }
+
+  int& count_;
+};
+
+constexpr bool test() {
+  int count = 0;
+  std::flat_set<int, CountingComp> set({0, 0}, CountingComp(count));
+  return set.size() == 1 && count == 2;
+}
+
+int main(int, char**) {
+  assert(test());
+#if TEST_STD_VER >= 26
+  static_assert(test());
+#endif
+
+  return 0;
+}
diff --git a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/initializer_list.pass.cpp
index 53e9210f32153..ef49a6ee9f986 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.map/flat.map.cons/initializer_list.pass.cpp
@@ -35,25 +35,6 @@ struct DefaultCtableComp {
   bool default_constructed_ = false;
 };
 
-struct CountingComp {
-  constexpr explicit CountingComp(int& count) : count_(count) {}
-
-  constexpr bool operator()(int x, int y) const {
-    ++count_;
-    return x < y;
-  }
-
-  int& count_;
-};
-
-void test_dedup_uses_one_direction_comparisons() {
-  int count = 0;
-  std::flat_map<int, int, CountingComp> map({{0, 1}, {0, 2}}, CountingComp(count));
-  assert(map.size() == 1);
-  assert(map.begin()->first == 0);
-  assert(count == 2);
-}
-
 template <template <class...> class KeyContainer, template <class...> class ValueContainer>
 constexpr void test() {
   std::pair<int, long> expected[] = {{1, 1}, {2, 2}, {3, 3}, {5, 2}};
@@ -187,7 +168,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  test_dedup_uses_one_direction_comparisons();
 #if TEST_STD_VER >= 26
   static_assert(test());
 #endif
diff --git a/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/initializer_list.pass.cpp b/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/initializer_list.pass.cpp
index ebf9781a2bc58..2a8e4c40ebe2b 100644
--- a/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/initializer_list.pass.cpp
+++ b/libcxx/test/std/containers/container.adaptors/flat.set/flat.set.cons/initializer_list.pass.cpp
@@ -35,24 +35,6 @@ struct DefaultCtableComp {
   bool default_constructed_ = false;
 };
 
-struct CountingComp {
-  constexpr explicit CountingComp(int& count) : count_(count) {}
-
-  constexpr bool operator()(int x, int y) const {
-    ++count_;
-    return x < y;
-  }
-
-  int& count_;
-};
-
-void test_dedup_uses_one_direction_comparisons() {
-  int count = 0;
-  std::flat_set<int, CountingComp> set({0, 0}, CountingComp(count));
-  assert(set.size() == 1);
-  assert(count == 2);
-}
-
 template <template <class...> class KeyContainer>
 constexpr void test() {
   int expected[] = {1, 2, 3, 5};
@@ -178,7 +160,6 @@ constexpr bool test() {
 
 int main(int, char**) {
   test();
-  test_dedup_uses_one_direction_comparisons();
 #if TEST_STD_VER >= 26
   static_assert(test());
 #endif



More information about the libcxx-commits mailing list