[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