[libcxx-commits] [libcxx] [libc++] Implement P2077R3: Heterogeneous erasure overloads for associative containers (PR #174680)
Rafail Shakhin ogly via libcxx-commits
libcxx-commits at lists.llvm.org
Fri Jun 26 12:54:28 PDT 2026
https://github.com/rsaddatimov updated https://github.com/llvm/llvm-project/pull/174680
>From f95a72fb08107ff1a6cc2c576cbf84f31ef5bf20 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 05:11:31 +0300
Subject: [PATCH 01/21] Initial commit: start with set & multiset tests
---
libcxx/include/__tree | 16 +--
libcxx/include/map | 10 +-
libcxx/include/set | 64 ++++++++-
.../multiset/extract.transparent.pass.cpp | 27 ++++
.../erase.transparent.pass.cpp | 27 ++++
.../set/erase.transparent.pass.cpp | 27 ++++
.../set/extract.transparent.pass.cpp | 27 ++++
.../support/test_transparent_associative.hpp | 124 ++++++++++++++++++
8 files changed, 307 insertions(+), 15 deletions(-)
create mode 100644 libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
create mode 100644 libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
create mode 100644 libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
create mode 100644 libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
create mode 100644 libcxx/test/support/test_transparent_associative.hpp
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 5ff1387bc25d2..0696a387bdfe2 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1239,10 +1239,10 @@ public:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
__node_handle_merge_multi(__tree<_Tp, _Comp2, _Allocator>& __source);
+ template <class _NodeHandle, class _Key>
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const _Key&);
template <class _NodeHandle>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(key_type const&);
- template <class _NodeHandle>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const_iterator);
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract_iterator(const_iterator);
#endif
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p);
@@ -2089,7 +2089,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __n
if (__nh.empty())
return _InsertReturnType{end(), false, _NodeHandle()};
- __node_pointer __ptr = __nh.__ptr_;
+ __node_pointer __ptr = __nh.__ptr_;
auto [__parent, __child] = __find_equal(__ptr->__get_value());
if (__child != nullptr)
return _InsertReturnType{
@@ -2120,19 +2120,19 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
}
template <class _Tp, class _Compare, class _Allocator>
-template <class _NodeHandle>
+template <class _NodeHandle, class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(key_type const& __key) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const& _Key __key) {
iterator __it = __lower_bound_multi(__key);
if (__it == end() || __value_comp_(__key, *__it))
return _NodeHandle();
- return __node_handle_extract<_NodeHandle>(__it);
+ return __node_handle_extract_iterator<_NodeHandle>(__it);
}
template <class _Tp, class _Compare, class _Allocator>
template <class _NodeHandle>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const_iterator __p) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract_iterator(const_iterator __p) {
__node_pointer __np = __p.__get_np();
__remove_node_pointer(__np);
return _NodeHandle(__np, __alloc());
diff --git a/libcxx/include/map b/libcxx/include/map
index 8efe18fa3f839..1dda078c4e6c1 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1037,8 +1037,8 @@ public:
# if _LIBCPP_STD_VER >= 14
template <class _InputIterator>
- _LIBCPP_HIDE_FROM_ABI
- _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+ map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
: map(__f, __l, key_compare(), __a) {}
# endif
@@ -1353,7 +1353,7 @@ public:
return __tree_.template __node_handle_extract<node_type>(__key);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
- return __tree_.template __node_handle_extract<node_type>(__it.__i_);
+ return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
}
template <class _Compare2>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
@@ -2022,8 +2022,8 @@ public:
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
return __tree_.template __node_handle_extract<node_type>(__key);
}
- [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
- return __tree_.template __node_handle_extract<node_type>(__it.__i_);
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+ return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
}
template <class _Compare2>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
diff --git a/libcxx/include/set b/libcxx/include/set
index 3f2d2cd164d2d..4ddafcb392da6 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -125,13 +125,19 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
+ template <typename K>
+ node_type extract(K&& k); // C++23
+
insert_return_type insert(node_type&& nh); // C++17
iterator insert(const_iterator hint, node_type&& nh); // C++17
iterator erase(const_iterator position);
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
+ template <typename K>
+ size_type erase(K&& k); // C++23
iterator erase(const_iterator first, const_iterator last);
+
void clear() noexcept;
template<class C2>
@@ -370,6 +376,9 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
+ template <typename K>
+ node_type extract(K&& k); // C++23
+
iterator insert(node_type&& nh); // C++17
iterator insert(const_iterator hint, node_type&& nh); // C++17
@@ -377,6 +386,9 @@ public:
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
iterator erase(const_iterator first, const_iterator last);
+ template <typename K>
+ size_type erase(K&& k); // C++23
+
void clear() noexcept;
template<class C2>
@@ -770,6 +782,18 @@ public:
_LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
_LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_unique(__k); }
_LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
+
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+ return __tree_.__erase_unique(__kp);
+ }
+
+# endif // _LIBCPP_STD_VER >= 23
+
_LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
# if _LIBCPP_STD_VER >= 17
@@ -787,8 +811,20 @@ public:
return __tree_.template __node_handle_extract<node_type>(__key);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
- return __tree_.template __node_handle_extract<node_type>(__it);
+ return __tree_.template __node_handle_extract_iterator<node_type>(__it);
+ }
+
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+ return __tree_.template __node_handle_extract<node_type>(__kp);
}
+
+# endif // _LIBCPP_STD_VER >= 23
+
template <class _Compare2>
_LIBCPP_HIDE_FROM_ABI void merge(set<key_type, _Compare2, allocator_type>& __source) {
_LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1259,6 +1295,18 @@ public:
_LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __p) { return __tree_.erase(__p); }
_LIBCPP_HIDE_FROM_ABI size_type erase(const key_type& __k) { return __tree_.__erase_multi(__k); }
_LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __f, const_iterator __l) { return __tree_.erase(__f, __l); }
+
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+ return __tree_.__erase_multi(__kp);
+ }
+
+# endif // _LIBCPP_STD_VER >= 23
+
_LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __tree_.clear(); }
# if _LIBCPP_STD_VER >= 17
@@ -1276,8 +1324,20 @@ public:
return __tree_.template __node_handle_extract<node_type>(__key);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
- return __tree_.template __node_handle_extract<node_type>(__it);
+ return __tree_.template __node_handle_extract_iterator<node_type>(__it);
}
+
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+ return __tree_.template __node_handle_extract<node_type>(__kp);
+ }
+
+# endif // _LIBCPP_STD_VER >= 23
+
template <class _Compare2>
_LIBCPP_HIDE_FROM_ABI void merge(multiset<key_type, _Compare2, allocator_type>& __source) {
_LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
diff --git a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..fc04c8d85c267
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <set>
+
+// class multiset
+
+// template<typename K>
+// node_type extract(K&& k) const; // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+ test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
+
+ test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_final>>({1, 2, 4});
+
+ test_non_transparent_extract<std::multiset<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..a7252f0aa039c
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <set>
+
+// class multiset
+
+// template<typename K>
+// size_type erase(K&& k) const; // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+ test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
+
+ test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_final>>({1, 2, 4, 5});
+
+ test_non_transparent_erase<std::multiset<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..d2ca1fe599374
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <set>
+
+// class set
+
+// template<typename K>
+// size_type erase(K&& k) const; // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+ test_transparent_erase<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
+
+ test_transparent_erase<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4, 5});
+
+ test_non_transparent_erase<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..62c72808b83d1
--- /dev/null
+++ b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <set>
+
+// class set
+
+// template<typename K>
+// node_type extract(K&& k) const; // C++23
+
+#include <set>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+ test_transparent_extract<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
+
+ test_transparent_extract<std::set<StoredType<int>, transparent_comparator_final>>({1, 2, 4});
+
+ test_non_transparent_extract<std::set<StoredType<int>, std::less<StoredType<int>>>>({1, 2});
+}
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
new file mode 100644
index 0000000000000..a59f10e426ecc
--- /dev/null
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -0,0 +1,124 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+#ifndef TEST_TRANSPARENT_ASSOCIATIVE_H
+#define TEST_TRANSPARENT_ASSOCIATIVE_H
+
+#include "test_macros.h"
+
+#include <cassert>
+
+#if TEST_STD_VER > 23
+
+template <typename T>
+struct StoredType;
+
+template <typename T>
+struct SearchedType {
+ explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+
+ operator StoredType<T>() const {
+ ++*conversions_;
+ return StoredType<T>{value_};
+ }
+
+ T get_value() const { return value_; }
+
+ auto operator<=>(const SearchedType<T>&) const = default;
+
+private:
+ T value_;
+ int* conversions_;
+};
+
+template <typename T>
+struct StoredType {
+ StoredType() = default;
+ StoredType(T value) : value_(value) {}
+
+ T get_value() const { return value_; }
+
+ auto operator<=>(const StoredType<T>&) const = default;
+
+private:
+ T value_;
+};
+
+struct transparent_comparator_base {
+ using is_transparent = void;
+
+ template <typename T>
+ bool operator()(const SearchedType<T>& lhs, const StoredType<T>& rhs) const {
+ return lhs.get_value() < rhs.get_value();
+ }
+
+ template <typename T>
+ bool operator()(const StoredType<T>& lhs, const SearchedType<T>& rhs) const {
+ return lhs.get_value() < rhs.get_value();
+ }
+
+ template <typename T>
+ bool operator()(const StoredType<T>& lhs, const StoredType<T>& rhs) const {
+ return lhs < rhs;
+ }
+};
+
+struct transparent_comparator_final final : public transparent_comparator_base {};
+
+template <class Container>
+void test_transparent_erase(Container c) {
+ int conversions = 0;
+ assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+ assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+ assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+
+ assert(conversions == 0);
+
+ c.erase(c.begin());
+ c.erase(c.cbegin());
+
+ assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_erase(Container c) {
+ int conversions = 0;
+ assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+ assert(conversions == 1);
+ assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+ assert(conversions == 2);
+ assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+ assert(conversions == 3);
+}
+
+template <class Container>
+void test_transparent_extract(Container c) {
+ int conversions = 0;
+ assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+ assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+ assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+ assert(conversions == 0);
+
+ assert(!c.extract(c.cbegin()).empty());
+ assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_extract(Container c) {
+ int conversions = 0;
+ assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+ assert(conversions == 1);
+ assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+ assert(conversions == 2);
+ assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+ assert(conversions == 3);
+}
+
+#endif // TEST_STD_VER > 23
+
+#endif // TEST_TRANSPARENT_ASSOCIATIVE_H
>From 435f7e77c1dc6dd8806ffc0894034edfc5c1c136 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 06:22:38 +0300
Subject: [PATCH 02/21] Fix test & version stuff
---
libcxx/docs/FeatureTestMacroTable.rst | 2 +-
libcxx/docs/ReleaseNotes/22.rst | 1 +
libcxx/docs/Status/Cxx23Papers.csv | 2 +-
libcxx/include/version | 2 +-
.../map.version.compile.pass.cpp | 32 ++++++-------------
.../set.version.compile.pass.cpp | 32 ++++++-------------
.../unordered_map.version.compile.pass.cpp | 32 ++++++-------------
.../unordered_set.version.compile.pass.cpp | 32 ++++++-------------
.../version.version.compile.pass.cpp | 32 ++++++-------------
.../support/test_transparent_associative.hpp | 4 +--
.../generate_feature_test_macro_components.py | 1 -
11 files changed, 56 insertions(+), 116 deletions(-)
diff --git a/libcxx/docs/FeatureTestMacroTable.rst b/libcxx/docs/FeatureTestMacroTable.rst
index 61f0ced9f0282..6360ea686ef71 100644
--- a/libcxx/docs/FeatureTestMacroTable.rst
+++ b/libcxx/docs/FeatureTestMacroTable.rst
@@ -314,7 +314,7 @@ Status
---------------------------------------------------------- -----------------
``__cpp_lib_allocate_at_least`` ``202302L``
---------------------------------------------------------- -----------------
- ``__cpp_lib_associative_heterogeneous_erasure`` *unimplemented*
+ ``__cpp_lib_associative_heterogeneous_erasure`` ``202110L``
---------------------------------------------------------- -----------------
``__cpp_lib_bind_back`` ``202202L``
---------------------------------------------------------- -----------------
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 9f9927095a61f..5b05666cbb419 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -38,6 +38,7 @@ What's New in Libc++ 22.0.0?
Implemented Papers
------------------
+- P2077R3: Heterogeneous erasure overloads for associative containers (`Github <https://llvm.org/PR105165>`__)
- P2592R3: Hashing support for ``std::chrono`` value classes (`Github <https://llvm.org/PR105358>`__)
- P2321R2: ``zip`` (`Github <https://llvm.org/PR105169>`__)
- P2988R12: ``std::optional<T&>`` (`Github <https://llvm.org/PR148131>`__)
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index 739f0ecab37e7..d6fa695026543 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -30,7 +30,7 @@
"`P1147R1 <https://wg21.link/P1147R1>`__","Printing ``volatile`` Pointers","2021-10 (Virtual)","|Complete|","14","`#105161 <https://github.com/llvm/llvm-project/issues/105161>`__",""
"`P1272R4 <https://wg21.link/P1272R4>`__","Byteswapping for fun&&nuf","2021-10 (Virtual)","|Complete|","14","`#105163 <https://github.com/llvm/llvm-project/issues/105163>`__",""
"`P1675R2 <https://wg21.link/P1675R2>`__","``rethrow_exception`` must be allowed to copy","2021-10 (Virtual)","|Nothing To Do|","","`#105164 <https://github.com/llvm/llvm-project/issues/105164>`__",""
-"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","","","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
+"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","Complete","22","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
"`P2251R1 <https://wg21.link/P2251R1>`__","Require ``span`` & ``basic_string_view`` to be Trivially Copyable","2021-10 (Virtual)","|Complete|","14","`#105166 <https://github.com/llvm/llvm-project/issues/105166>`__",""
"`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","","","`#105167 <https://github.com/llvm/llvm-project/issues/105167>`__",""
"`P2321R2 <https://wg21.link/P2321R2>`__","``zip``","2021-10 (Virtual)","|Complete|","22","`#105169 <https://github.com/llvm/llvm-project/issues/105169>`__",""
diff --git a/libcxx/include/version b/libcxx/include/version
index fa03b46f4475b..01379ea6bb39a 100644
--- a/libcxx/include/version
+++ b/libcxx/include/version
@@ -492,7 +492,7 @@ __cpp_lib_void_t 201411L <type_traits>
#if _LIBCPP_STD_VER >= 23
# define __cpp_lib_adaptor_iterator_pair_constructor 202106L
# define __cpp_lib_allocate_at_least 202302L
-// # define __cpp_lib_associative_heterogeneous_erasure 202110L
+# define __cpp_lib_associative_heterogeneous_erasure 202110L
# define __cpp_lib_bind_back 202202L
# define __cpp_lib_byteswap 202110L
# define __cpp_lib_constexpr_bitset 202207L
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
index 794c2ee9ad086..587c90a02fec4 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/map.version.compile.pass.cpp
@@ -247,17 +247,11 @@
# error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-# endif
-# if __cpp_lib_associative_heterogeneous_erasure != 202110L
-# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-# endif
-# else
-# ifdef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_associative_heterogeneous_erasure
+# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+# endif
+# if __cpp_lib_associative_heterogeneous_erasure != 202110L
+# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
# endif
# ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -332,17 +326,11 @@
# error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-# endif
-# if __cpp_lib_associative_heterogeneous_erasure != 202110L
-# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_associative_heterogeneous_erasure
+# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+# endif
+# if __cpp_lib_associative_heterogeneous_erasure != 202110L
+# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
index 5dc69f29d0ecd..abc32dad7e7c9 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/set.version.compile.pass.cpp
@@ -193,17 +193,11 @@
# error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-# endif
-# if __cpp_lib_associative_heterogeneous_erasure != 202110L
-# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-# endif
-# else
-# ifdef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_associative_heterogeneous_erasure
+# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+# endif
+# if __cpp_lib_associative_heterogeneous_erasure != 202110L
+# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
# endif
# ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -254,17 +248,11 @@
# error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-# endif
-# if __cpp_lib_associative_heterogeneous_erasure != 202110L
-# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_associative_heterogeneous_erasure
+# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+# endif
+# if __cpp_lib_associative_heterogeneous_erasure != 202110L
+# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
index 221d8aaebc14b..ba777e5434008 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_map.version.compile.pass.cpp
@@ -225,17 +225,11 @@
# error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-# endif
-# if __cpp_lib_associative_heterogeneous_erasure != 202110L
-# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-# endif
-# else
-# ifdef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_associative_heterogeneous_erasure
+# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+# endif
+# if __cpp_lib_associative_heterogeneous_erasure != 202110L
+# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
# endif
# ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -306,17 +300,11 @@
# error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-# endif
-# if __cpp_lib_associative_heterogeneous_erasure != 202110L
-# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_associative_heterogeneous_erasure
+# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+# endif
+# if __cpp_lib_associative_heterogeneous_erasure != 202110L
+# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
index d1c1335df7c80..b168215ea737b 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/unordered_set.version.compile.pass.cpp
@@ -187,17 +187,11 @@
# error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-# endif
-# if __cpp_lib_associative_heterogeneous_erasure != 202110L
-# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-# endif
-# else
-# ifdef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_associative_heterogeneous_erasure
+# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+# endif
+# if __cpp_lib_associative_heterogeneous_erasure != 202110L
+# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
# endif
# ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -248,17 +242,11 @@
# error "__cpp_lib_allocator_traits_is_always_equal should have the value 201411L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-# endif
-# if __cpp_lib_associative_heterogeneous_erasure != 202110L
-# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_associative_heterogeneous_erasure
+# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+# endif
+# if __cpp_lib_associative_heterogeneous_erasure != 202110L
+# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
index 7a88d5127d887..47523ce9c1576 100644
--- a/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
+++ b/libcxx/test/std/language.support/support.limits/support.limits.general/version.version.compile.pass.cpp
@@ -4664,17 +4664,11 @@
# error "__cpp_lib_as_const should have the value 201510L in c++23"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
-# endif
-# if __cpp_lib_associative_heterogeneous_erasure != 202110L
-# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
-# endif
-# else
-# ifdef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_associative_heterogeneous_erasure
+# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++23"
+# endif
+# if __cpp_lib_associative_heterogeneous_erasure != 202110L
+# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++23"
# endif
# ifdef __cpp_lib_associative_heterogeneous_insertion
@@ -6353,17 +6347,11 @@
# error "__cpp_lib_as_const should have the value 201510L in c++26"
# endif
-# if !defined(_LIBCPP_VERSION)
-# ifndef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
-# endif
-# if __cpp_lib_associative_heterogeneous_erasure != 202110L
-# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
-# endif
-# else
-# ifdef __cpp_lib_associative_heterogeneous_erasure
-# error "__cpp_lib_associative_heterogeneous_erasure should not be defined because it is unimplemented in libc++!"
-# endif
+# ifndef __cpp_lib_associative_heterogeneous_erasure
+# error "__cpp_lib_associative_heterogeneous_erasure should be defined in c++26"
+# endif
+# if __cpp_lib_associative_heterogeneous_erasure != 202110L
+# error "__cpp_lib_associative_heterogeneous_erasure should have the value 202110L in c++26"
# endif
# if !defined(_LIBCPP_VERSION)
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index a59f10e426ecc..6ec13d29000ea 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -13,7 +13,7 @@
#include <cassert>
-#if TEST_STD_VER > 23
+#if TEST_STD_VER >= 23
template <typename T>
struct StoredType;
@@ -119,6 +119,6 @@ void test_non_transparent_extract(Container c) {
assert(conversions == 3);
}
-#endif // TEST_STD_VER > 23
+#endif // TEST_STD_VER >= 23
#endif // TEST_TRANSPARENT_ASSOCIATIVE_H
diff --git a/libcxx/utils/generate_feature_test_macro_components.py b/libcxx/utils/generate_feature_test_macro_components.py
index c94c27db7a472..93a17be11936d 100644
--- a/libcxx/utils/generate_feature_test_macro_components.py
+++ b/libcxx/utils/generate_feature_test_macro_components.py
@@ -152,7 +152,6 @@ def add_version_header(tc):
"name": "__cpp_lib_associative_heterogeneous_erasure",
"values": {"c++23": 202110},
"headers": ["map", "set", "unordered_map", "unordered_set"],
- "unimplemented": True,
},
{
"name": "__cpp_lib_associative_heterogeneous_insertion",
>From 6887070ad738e64fd03b79a8d48ed817ff5752d1 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 17:10:53 +0300
Subject: [PATCH 03/21] Don't uglify nodiscard
---
libcxx/include/set | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/libcxx/include/set b/libcxx/include/set
index 4ddafcb392da6..62c4f2bbb59e2 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -819,7 +819,7 @@ public:
template <class _Kp>
requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
!is_convertible_v<_Kp &&, const_iterator>)
- [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
return __tree_.template __node_handle_extract<node_type>(__kp);
}
@@ -1332,7 +1332,7 @@ public:
template <class _Kp>
requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
!is_convertible_v<_Kp &&, const_iterator>)
- [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
return __tree_.template __node_handle_extract<node_type>(__kp);
}
>From 99ad5686d6b8d1dca5c97032f5a1907aea647c13 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 17:29:05 +0300
Subject: [PATCH 04/21] Overloads for map & multimap
---
libcxx/include/map | 59 +++++++++++++++++++
.../map.erasure/erase.transparent.pass.cpp | 28 +++++++++
.../extract.transparent.pass.cpp | 27 +++++++++
.../erase.transparent.pass.cpp | 29 +++++++++
.../extract.transparent.pass.cpp | 27 +++++++++
5 files changed, 170 insertions(+)
create mode 100644 libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
create mode 100644 libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
create mode 100644 libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
create mode 100644 libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
diff --git a/libcxx/include/map b/libcxx/include/map
index 1dda078c4e6c1..2266dbaabca9f 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -147,6 +147,9 @@ public:
constexpr node_type extract(const_iterator position); // C++17
constexpr node_type extract(const key_type& x); // C++17
+ template <typename K>
+ constexpr node_type extract(K&& k); // C++23
+
constexpr insert_return_type insert(node_type&& nh); // C++17
constexpr iterator insert(const_iterator hint, node_type&& nh); // C++17
@@ -170,7 +173,10 @@ public:
constexpr iterator erase(const_iterator position);
constexpr iterator erase(iterator position); // C++14
constexpr size_type erase(const key_type& k);
+ template <typename K>
+ constexpr size_type erase(K&& k); // C++23
constexpr iterator erase(const_iterator first, const_iterator last);
+
constexpr void clear() noexcept;
template<class C2>
@@ -426,13 +432,19 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
+ template <typename K>
+ node_type extract(K&& k); // C++23
+
iterator insert(node_type&& nh); // C++17
iterator insert(const_iterator hint, node_type&& nh); // C++17
iterator erase(const_iterator position);
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
+ template <typename K>
+ size_type erase(K&& k); // C++23
iterator erase(const_iterator first, const_iterator last);
+
void clear() noexcept;
template<class C2>
@@ -1336,6 +1348,18 @@ public:
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __f, const_iterator __l) {
return __tree_.erase(__f.__i_, __l.__i_);
}
+
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+ return __tree_.__erase_unique(__kp);
+ }
+
+# endif // _LIBCPP_STD_VER >= 23
+
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void clear() _NOEXCEPT { __tree_.clear(); }
# if _LIBCPP_STD_VER >= 17
@@ -1355,6 +1379,18 @@ public:
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
}
+
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+ return __tree_.template __node_handle_extract<node_type>(__kp);
+ }
+
+# endif // _LIBCPP_STD_VER >= 23
+
template <class _Compare2>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
merge(map<key_type, mapped_type, _Compare2, allocator_type>& __source) {
@@ -2008,6 +2044,17 @@ public:
return __tree_.erase(__f.__i_, __l.__i_);
}
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+ return __tree_.__erase_multi(__kp);
+ }
+
+# endif // _LIBCPP_STD_VER >= 23
+
# if _LIBCPP_STD_VER >= 17
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator insert(node_type&& __nh) {
_LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(__nh.empty() || __nh.get_allocator() == get_allocator(),
@@ -2025,6 +2072,18 @@ public:
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
}
+
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<_Compare> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+ return __tree_.template __node_handle_extract<node_type>(__kp);
+ }
+
+# endif // _LIBCPP_STD_VER >= 23
+
template <class _Compare2>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 void
merge(multimap<key_type, mapped_type, _Compare2, allocator_type>& __source) {
diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..2fe83c20acf67
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <map>
+
+// class map
+
+// template<typename K>
+// size_type erase(K&& k) const; // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+ test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+ test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_final>>(
+ {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+ test_non_transparent_erase<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..0dd4d9afdeb9b
--- /dev/null
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <map>
+
+// class map
+
+// template<typename K>
+// node_type extract(K&& k) const; // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+ test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
+
+ test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
+
+ test_non_transparent_extract<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..7e3f625370d73
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -0,0 +1,29 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <map>
+
+// class multimap
+
+// template<typename K>
+// size_type erase(K&& k) const; // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+ test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
+ {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+ test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_final>>(
+ {{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+
+ test_non_transparent_erase<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..9faa74f69e2b0
--- /dev/null
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <map>
+
+// class multimap
+
+// template<typename K>
+// node_type extract(K&& k) const; // C++23
+
+#include <map>
+#include "test_transparent_associative.hpp"
+
+int main(int, char**) {
+ test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
+
+ test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
+
+ test_non_transparent_extract<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+}
>From bac761092c6a857eddf26c8be7670375681f3526 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 18:12:03 +0300
Subject: [PATCH 05/21] Overloads for unordered_map & unordered_multimap
---
libcxx/include/__hash_table | 16 +--
libcxx/include/unordered_map | 62 +++++++++++-
libcxx/include/unordered_set | 4 +-
.../unord.map/erase.transparent.pass.cpp | 56 +++++++++++
.../extract.transparent.pass.cpp | 56 +++++++++++
.../unord.multimap/erase.transparent.pass.cpp | 56 +++++++++++
.../extract.transparent.pass.cpp | 56 +++++++++++
.../test/support/test_transparent_unordered.h | 99 ++++++++++++++-----
8 files changed, 366 insertions(+), 39 deletions(-)
create mode 100644 libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
create mode 100644 libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
create mode 100644 libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
create mode 100644 libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index 0ce4cf3edd6fd..ff3d0f1ab7749 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -896,10 +896,10 @@ public:
template <class _Table>
_LIBCPP_HIDE_FROM_ABI void __node_handle_merge_multi(_Table& __source);
+ template <class _NodeHandle, class _Key>
+ _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const _Key& __key);
template <class _NodeHandle>
- _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(key_type const& __key);
- template <class _NodeHandle>
- _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const_iterator __it);
+ _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_iterator(const_iterator __it);
#endif
_LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
@@ -1648,18 +1648,18 @@ __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_insert_unique(const_iter
}
template <class _Tp, class _Hash, class _Equal, class _Alloc>
-template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle
-__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(key_type const& __key) {
+template <class _NodeHandle, class _Key>
+_LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(const _Key& __key) {
iterator __i = find(__key);
if (__i == end())
return _NodeHandle();
- return __node_handle_extract<_NodeHandle>(__i);
+ return __node_handle_extract_iterator<_NodeHandle>(__i);
}
template <class _Tp, class _Hash, class _Equal, class _Alloc>
template <class _NodeHandle>
-_LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract(const_iterator __p) {
+_LIBCPP_HIDE_FROM_ABI _NodeHandle
+__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract_iterator(const_iterator __p) {
allocator_type __alloc(__node_alloc());
return _NodeHandle(remove(__p).release(), __alloc);
}
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 8cefba01fb5f6..2a9bc016a4e9a 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -139,6 +139,9 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
+ template <typename K>
+ size_type erase(K&& k); // C++23
+
insert_return_type insert(node_type&& nh); // C++17
iterator insert(const_iterator hint, node_type&& nh); // C++17
@@ -162,7 +165,10 @@ public:
iterator erase(const_iterator position);
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
+ template <typename K>
+ size_type erase(K&& k); // C++23
iterator erase(const_iterator first, const_iterator last);
+
void clear() noexcept;
template<class H2, class P2>
@@ -425,13 +431,19 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
+ template <typename K>
+ node_type extract(K&& k); // C++23
+
iterator insert(node_type&& nh); // C++17
iterator insert(const_iterator hint, node_type&& nh); // C++17
iterator erase(const_iterator position);
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
+ template <typename K>
+ size_type erase(K&& k); // C++23
iterator erase(const_iterator first, const_iterator last);
+
void clear() noexcept;
template<class H2, class P2>
@@ -1158,6 +1170,18 @@ public:
_LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
return __table_.erase(__first.__i_, __last.__i_);
}
+
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+ return __table_.__erase_unique(__kp);
+ }
+
+# endif // _LIBCPP_STD_VER >= 23
+
_LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
# if _LIBCPP_STD_VER >= 17
@@ -1175,9 +1199,20 @@ public:
return __table_.template __node_handle_extract<node_type>(__key);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
- return __table_.template __node_handle_extract<node_type>(__it.__i_);
+ return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+ }
+
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+ return __table_.template __node_handle_extract<node_type>(__kp);
}
+# endif // _LIBCPP_STD_VER >= 23
+
template <class _H2, class _P2>
_LIBCPP_HIDE_FROM_ABI void merge(unordered_map<key_type, mapped_type, _H2, _P2, allocator_type>& __source) {
_LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1896,6 +1931,18 @@ public:
_LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
return __table_.erase(__first.__i_, __last.__i_);
}
+
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+ return __table_.__erase_multi(__kp);
+ }
+
+# endif // _LIBCPP_STD_VER >= 23
+
_LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
# if _LIBCPP_STD_VER >= 17
@@ -1913,9 +1960,20 @@ public:
return __table_.template __node_handle_extract<node_type>(__key);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
- return __table_.template __node_handle_extract<node_type>(__it.__i_);
+ return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+ }
+
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+ return __table_.template __node_handle_extract<node_type>(__kp);
}
+# endif // _LIBCPP_STD_VER >= 23
+
template <class _H2, class _P2>
_LIBCPP_HIDE_FROM_ABI void merge(unordered_multimap<key_type, mapped_type, _H2, _P2, allocator_type>& __source) {
_LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index d54d59403b038..29bd03e6d1c5a 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -805,7 +805,7 @@ public:
return __table_.template __node_handle_extract<node_type>(__key);
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
- return __table_.template __node_handle_extract<node_type>(__it);
+ return __table_.template __node_handle_extract_iterator<node_type>(__it);
}
template <class _H2, class _P2>
@@ -1412,7 +1412,7 @@ public:
return __table_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __position) {
- return __table_.template __node_handle_extract<node_type>(__position);
+ return __table_.template __node_handle_extract_iterator<node_type>(__position);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
return __table_.template __node_handle_extract<node_type>(__key);
diff --git a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..2d0138ef6472f
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <unordered_map>
+
+// class unordered_map
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+ using key_type = StoredType<int>;
+
+ {
+ // Make sure conversions don't happen for transparent non-final hasher and key_equal
+ using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<>>;
+ test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+ }
+
+ {
+ // Make sure conversions don't happen for transparent final hasher and key_equal
+ using M = unord_map_type<std::unordered_map, transparent_hash_final, transparent_equal_final>;
+ test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent hasher
+ using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<>>;
+ test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent key_equal
+ using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+ }
+
+ {
+ // Make sure conversions do happen for both non-transparent hasher and key_equal
+ using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..08a2e05e777bc
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <unordered_map>
+
+// class unordered_map
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+ using key_type = StoredType<int>;
+
+ {
+ // Make sure conversions don't happen for transparent non-final hasher and key_equal
+ using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<>>;
+ test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+ }
+
+ {
+ // Make sure conversions don't happen for transparent final hasher and key_equal
+ using M = unord_map_type<std::unordered_map, transparent_hash_final, transparent_equal_final>;
+ test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent hasher
+ using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<>>;
+ test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent key_equal
+ using M = unord_map_type<std::unordered_map, transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+ }
+
+ {
+ // Make sure conversions do happen for both non-transparent hasher and key_equal
+ using M = unord_map_type<std::unordered_map, non_transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..b15d915a618d9
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <unordered_map>
+
+// class unordered_multimap
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+ using key_type = StoredType<int>;
+
+ {
+ // Make sure conversions don't happen for transparent non-final hasher and key_equal
+ using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<>>;
+ test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+ }
+
+ {
+ // Make sure conversions don't happen for transparent final hasher and key_equal
+ using M = unord_map_type<std::unordered_multimap, transparent_hash_final, transparent_equal_final>;
+ test_transparent_erase<M>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent hasher
+ using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<>>;
+ test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent key_equal
+ using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+ }
+
+ {
+ // Make sure conversions do happen for both non-transparent hasher and key_equal
+ using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_erase<M>({{1, 0}, {2, 0}});
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..8eefc2b5f689b
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <unordered_map>
+
+// class unordered_multimap
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_map>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+ using key_type = StoredType<int>;
+
+ {
+ // Make sure conversions don't happen for transparent non-final hasher and key_equal
+ using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<>>;
+ test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+ }
+
+ {
+ // Make sure conversions don't happen for transparent final hasher and key_equal
+ using M = unord_map_type<std::unordered_multimap, transparent_hash_final, transparent_equal_final>;
+ test_transparent_extract<M>({{1, 0}, {2, 0}, {4, 0}});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent hasher
+ using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<>>;
+ test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent key_equal
+ using M = unord_map_type<std::unordered_multimap, transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+ }
+
+ {
+ // Make sure conversions do happen for both non-transparent hasher and key_equal
+ using M = unord_map_type<std::unordered_multimap, non_transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_extract<M>({{1, 0}, {2, 0}});
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index e2d02402d808b..f77688b42e194 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -46,7 +46,7 @@ struct transparent_equal_final final : std::equal_to<> {};
template <typename T>
struct SearchedType {
- explicit SearchedType(T value, int *counter) : value_(value), conversions_(counter) { }
+ explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
// Whenever a conversion is performed, increment the counter to keep track
// of conversions.
@@ -55,48 +55,40 @@ struct SearchedType {
return StoredType<T>{value_};
}
- int get_value() const {
- return value_;
- }
+ int get_value() const { return value_; }
private:
T value_;
- int *conversions_;
+ int* conversions_;
};
template <typename T>
struct StoredType {
StoredType() = default;
- StoredType(T value) : value_(value) { }
+ StoredType(T value) : value_(value) {}
- friend bool operator==(StoredType const& lhs, StoredType const& rhs) {
- return lhs.value_ == rhs.value_;
- }
+ friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
// If we're being passed a SearchedType<T> object, avoid the conversion
// to T. This allows testing that the transparent operations are correctly
// forwarding the SearchedType all the way to this comparison by checking
// that we didn't have a conversion when we search for a SearchedType<T>
// in a container full of StoredType<T>.
- friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
- return lhs.value_ == rhs.get_value();
- }
+ friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
- int get_value() const {
- return value_;
- }
+ int get_value() const { return value_; }
private:
T value_;
};
-template<template<class...> class UnorderedSet, class Hash, class Equal>
+template <template <class...> class UnorderedSet, class Hash, class Equal>
using unord_set_type = UnorderedSet<StoredType<int>, Hash, Equal>;
-template<template<class...> class UnorderedMap, class Hash, class Equal>
+template <template <class...> class UnorderedMap, class Hash, class Equal>
using unord_map_type = UnorderedMap<StoredType<int>, int, Hash, Equal>;
-template<class Container>
+template <class Container>
void test_transparent_find(Container c) {
int conversions = 0;
assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -105,7 +97,7 @@ void test_transparent_find(Container c) {
assert(conversions == 0);
}
-template<class Container>
+template <class Container>
void test_non_transparent_find(Container c) {
int conversions = 0;
assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -116,7 +108,7 @@ void test_non_transparent_find(Container c) {
assert(conversions == 3);
}
-template<class Container>
+template <class Container>
void test_transparent_count(Container c) {
int conversions = 0;
assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -125,7 +117,7 @@ void test_transparent_count(Container c) {
assert(conversions == 0);
}
-template<class Container>
+template <class Container>
void test_non_transparent_count(Container c) {
int conversions = 0;
assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -136,7 +128,7 @@ void test_non_transparent_count(Container c) {
assert(conversions == 3);
}
-template<class Container>
+template <class Container>
void test_transparent_contains(Container c) {
int conversions = 0;
assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -145,7 +137,7 @@ void test_transparent_contains(Container c) {
assert(conversions == 0);
}
-template<class Container>
+template <class Container>
void test_non_transparent_contains(Container c) {
int conversions = 0;
assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -156,10 +148,10 @@ void test_non_transparent_contains(Container c) {
assert(conversions == 3);
}
-template<class Container>
+template <class Container>
void test_transparent_equal_range(Container c) {
int conversions = 0;
- auto iters = c.equal_range(SearchedType<int>(1, &conversions));
+ auto iters = c.equal_range(SearchedType<int>(1, &conversions));
assert(std::distance(iters.first, iters.second) > 0);
iters = c.equal_range(SearchedType<int>(2, &conversions));
assert(std::distance(iters.first, iters.second) > 0);
@@ -168,10 +160,10 @@ void test_transparent_equal_range(Container c) {
assert(conversions == 0);
}
-template<class Container>
+template <class Container>
void test_non_transparent_equal_range(Container c) {
int conversions = 0;
- auto iters = c.equal_range(SearchedType<int>(1, &conversions));
+ auto iters = c.equal_range(SearchedType<int>(1, &conversions));
assert(std::distance(iters.first, iters.second) > 0);
assert(conversions == 1);
iters = c.equal_range(SearchedType<int>(2, &conversions));
@@ -182,6 +174,59 @@ void test_non_transparent_equal_range(Container c) {
assert(conversions == 3);
}
+# if TEST_STD_VER >= 23
+
+template <class Container>
+void test_transparent_erase(Container c) {
+ int conversions = 0;
+
+ assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+ assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+ assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+ assert(conversions == 0);
+
+ c.erase(c.begin());
+ c.erase(c.cbegin());
+
+ assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_erase(Container c) {
+ int conversions = 0;
+ assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
+ assert(conversions == 1);
+ assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
+ assert(conversions == 2);
+ assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
+ assert(conversions == 3);
+}
+
+template <class Container>
+void test_transparent_extract(Container c) {
+ int conversions = 0;
+ assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+ assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+ assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+ assert(conversions == 0);
+
+ assert(!c.extract(c.cbegin()).empty());
+ assert(c.empty());
+}
+
+template <class Container>
+void test_non_transparent_extract(Container c) {
+ int conversions = 0;
+ assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
+ assert(conversions == 1);
+ assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+ assert(conversions == 2);
+ assert(c.extract(SearchedType<int>(3, &conversions)).empty());
+ assert(conversions == 3);
+}
+
+# endif // TEST_STD_VER >= 23
+
#endif // TEST_STD_VER > 17
#endif // TEST_TRANSPARENT_UNORDERED_H
>From 82c24b41748e1244995bc7b18343fb7779275a0a Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 19:12:42 +0300
Subject: [PATCH 06/21] Overloads for unordered_set & unordered_multiset
---
libcxx/include/unordered_map | 2 +-
libcxx/include/unordered_set | 58 +++++++++++++++++++
.../unord.multiset/erase.transparent.pass.cpp | 56 ++++++++++++++++++
.../extract.transparent.pass.cpp | 56 ++++++++++++++++++
.../unord.set/erase.transparent.pass.cpp | 56 ++++++++++++++++++
.../unord.set/extract.transparent.pass.cpp | 56 ++++++++++++++++++
.../test/support/test_transparent_unordered.h | 4 +-
7 files changed, 285 insertions(+), 3 deletions(-)
create mode 100644 libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
create mode 100644 libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
create mode 100644 libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
create mode 100644 libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index 2a9bc016a4e9a..ca2173def1f71 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -140,7 +140,7 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
template <typename K>
- size_type erase(K&& k); // C++23
+ node_type extract(K&& k); // C++23
insert_return_type insert(node_type&& nh); // C++17
iterator insert(const_iterator hint, node_type&& nh); // C++17
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 29bd03e6d1c5a..32a10c1be1e95 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -131,13 +131,19 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
+ template <typename K>
+ node_type extract(K&& k); // C++23
+
insert_return_type insert(node_type&& nh); // C++17
iterator insert(const_iterator hint, node_type&& nh); // C++17
iterator erase(const_iterator position);
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
+ template <typename K>
+ size_type erase(K&& k); // C++23
iterator erase(const_iterator first, const_iterator last);
+
void clear() noexcept;
template<class H2, class P2>
@@ -379,13 +385,19 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
+ template <typename K>
+ node_type extract(K&& k); // C++23
+
iterator insert(node_type&& nh); // C++17
iterator insert(const_iterator hint, node_type&& nh); // C++17
iterator erase(const_iterator position);
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
+ template <typename K>
+ size_type erase(K&& k); // C++23
iterator erase(const_iterator first, const_iterator last);
+
void clear() noexcept;
template<class H2, class P2>
@@ -788,6 +800,18 @@ public:
_LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
return __table_.erase(__first, __last);
}
+
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+ return __table_.__erase_unique(__kp);
+ }
+
+# endif // _LIBCPP_STD_VER >= 23
+
_LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
# if _LIBCPP_STD_VER >= 17
@@ -808,6 +832,17 @@ public:
return __table_.template __node_handle_extract_iterator<node_type>(__it);
}
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+ return __table_.template __node_handle_extract<node_type>(__kp);
+ }
+
+# endif // _LIBCPP_STD_VER >= 23
+
template <class _H2, class _P2>
_LIBCPP_HIDE_FROM_ABI void merge(unordered_set<key_type, _H2, _P2, allocator_type>& __source) {
_LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1418,6 +1453,17 @@ public:
return __table_.template __node_handle_extract<node_type>(__key);
}
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ [[nodiscard]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(_Kp&& __kp) {
+ return __table_.template __node_handle_extract<node_type>(__kp);
+ }
+
+# endif // _LIBCPP_STD_VER >= 23
+
template <class _H2, class _P2>
_LIBCPP_HIDE_FROM_ABI void merge(unordered_multiset<key_type, _H2, _P2, allocator_type>& __source) {
_LIBCPP_ASSERT_COMPATIBLE_ALLOCATOR(
@@ -1449,6 +1495,18 @@ public:
_LIBCPP_HIDE_FROM_ABI iterator erase(const_iterator __first, const_iterator __last) {
return __table_.erase(__first, __last);
}
+
+# if _LIBCPP_STD_VER >= 23
+
+ template <class _Kp>
+ requires(__is_transparent_v<hasher> && __is_transparent_v<key_equal> && !is_convertible_v<_Kp &&, iterator> &&
+ !is_convertible_v<_Kp &&, const_iterator>)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 size_type erase(_Kp&& __kp) {
+ return __table_.__erase_multi(__kp);
+ }
+
+# endif // _LIBCPP_STD_VER >= 23
+
_LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT { __table_.clear(); }
_LIBCPP_HIDE_FROM_ABI void swap(unordered_multiset& __u) _NOEXCEPT_(__is_nothrow_swappable_v<__table>) {
diff --git a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..41420d41613b1
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <unordered_set>
+
+// class unordered_multiset
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+ using key_type = StoredType<int>;
+
+ {
+ // Make sure conversions don't happen for transparent non-final hasher and key_equal
+ using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<>>;
+ test_transparent_erase<S>({1, 2, 4, 5});
+ }
+
+ {
+ // Make sure conversions don't happen for transparent final hasher and key_equal
+ using S = unord_set_type<std::unordered_multiset, transparent_hash_final, transparent_equal_final>;
+ test_transparent_erase<S>({1, 2, 4, 5});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent hasher
+ using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<>>;
+ test_non_transparent_erase<S>({1, 2});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent key_equal
+ using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_erase<S>({1, 2});
+ }
+
+ {
+ // Make sure conversions do happen for both non-transparent hasher and key_equal
+ using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_erase<S>({1, 2});
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..e094201a8cedd
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <unordered_set>
+
+// class unordered_multiset
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+ using key_type = StoredType<int>;
+
+ {
+ // Make sure conversions don't happen for transparent non-final hasher and key_equal
+ using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<>>;
+ test_transparent_extract<S>({1, 2, 4});
+ }
+
+ {
+ // Make sure conversions don't happen for transparent final hasher and key_equal
+ using S = unord_set_type<std::unordered_multiset, transparent_hash_final, transparent_equal_final>;
+ test_transparent_extract<S>({1, 2, 4});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent hasher
+ using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<>>;
+ test_non_transparent_extract<S>({1, 2});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent key_equal
+ using S = unord_set_type<std::unordered_multiset, transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_extract<S>({1, 2});
+ }
+
+ {
+ // Make sure conversions do happen for both non-transparent hasher and key_equal
+ using S = unord_set_type<std::unordered_multiset, non_transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_extract<S>({1, 2});
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
new file mode 100644
index 0000000000000..efc55a0ea8964
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <unordered_set>
+
+// class unordered_set
+
+// template <typename K>
+// size_type erase(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+ using key_type = StoredType<int>;
+
+ {
+ // Make sure conversions don't happen for transparent non-final hasher and key_equal
+ using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<>>;
+ test_transparent_erase<S>({1, 2, 4, 5});
+ }
+
+ {
+ // Make sure conversions don't happen for transparent final hasher and key_equal
+ using S = unord_set_type<std::unordered_set, transparent_hash_final, transparent_equal_final>;
+ test_transparent_erase<S>({1, 2, 4, 5});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent hasher
+ using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<>>;
+ test_non_transparent_erase<S>({1, 2});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent key_equal
+ using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_erase<S>({1, 2});
+ }
+
+ {
+ // Make sure conversions do happen for both non-transparent hasher and key_equal
+ using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_erase<S>({1, 2});
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
new file mode 100644
index 0000000000000..e84facdaabcb0
--- /dev/null
+++ b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
@@ -0,0 +1,56 @@
+//===----------------------------------------------------------------------===//
+//
+// 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
+//
+//===----------------------------------------------------------------------===//
+
+// REQUIRES: std-at-least-c++23
+
+// <unordered_set>
+
+// class unordered_set
+
+// template <typename K>
+// node_type extract(K&& k);
+
+#include <unordered_set>
+
+#include "test_transparent_unordered.h"
+
+int main(int, char**) {
+ using key_type = StoredType<int>;
+
+ {
+ // Make sure conversions don't happen for transparent non-final hasher and key_equal
+ using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<>>;
+ test_transparent_extract<S>({1, 2, 4});
+ }
+
+ {
+ // Make sure conversions don't happen for transparent final hasher and key_equal
+ using S = unord_set_type<std::unordered_set, transparent_hash_final, transparent_equal_final>;
+ test_transparent_extract<S>({1, 2, 4});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent hasher
+ using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<>>;
+ test_non_transparent_extract<S>({1, 2});
+ }
+
+ {
+ // Make sure conversions do happen for non-transparent key_equal
+ using S = unord_set_type<std::unordered_set, transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_extract<S>({1, 2});
+ }
+
+ {
+ // Make sure conversions do happen for both non-transparent hasher and key_equal
+ using S = unord_set_type<std::unordered_set, non_transparent_hash, std::equal_to<key_type>>;
+ test_non_transparent_extract<S>({1, 2});
+ }
+
+ return 0;
+}
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index f77688b42e194..f2439be716d44 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -55,7 +55,7 @@ struct SearchedType {
return StoredType<T>{value_};
}
- int get_value() const { return value_; }
+ T get_value() const { return value_; }
private:
T value_;
@@ -76,7 +76,7 @@ struct StoredType {
// in a container full of StoredType<T>.
friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
- int get_value() const { return value_; }
+ T get_value() const { return value_; }
private:
T value_;
>From 87669ce422ea198db43fa3bfdeeb3a77e139e7eb Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Wed, 7 Jan 2026 19:22:02 +0300
Subject: [PATCH 07/21] nodiscard test cases
---
.../containers/associative/map/map.nodiscard.verify.cpp | 4 ++++
.../containers/associative/map/multimap.nodiscard.verify.cpp | 4 ++++
.../containers/associative/set/multiset.nodiscard.verify.cpp | 4 ++++
.../containers/associative/set/set.nodiscard.verify.cpp | 4 ++++
.../libcxx/containers/unord/unord.map/nodiscard.verify.cpp | 4 ++++
.../containers/unord/unord.multimap/nodiscard.verify.cpp | 4 ++++
.../containers/unord/unord.multiset/nodiscard.verify.cpp | 4 ++++
.../libcxx/containers/unord/unord.set/nodiscard.verify.cpp | 4 ++++
8 files changed, 32 insertions(+)
diff --git a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
index 20218b50e6c60..7a418502a722b 100644
--- a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
@@ -121,4 +121,8 @@ void test() {
tm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+
+#if TEST_STD_VER >= 23
+ tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
}
diff --git a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
index 99e4fbceabf12..49ac8c6a76064 100644
--- a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
@@ -106,4 +106,8 @@ void test() {
tm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+
+#if TEST_STD_VER >= 23
+ tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
}
diff --git a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
index 889b3cc62d191..a90f69fc96af4 100644
--- a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
@@ -104,4 +104,8 @@ void test() {
ts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+
+#if TEST_STD_VER >= 23
+ ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
}
diff --git a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
index 0453541d1b862..02f2634363679 100644
--- a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
@@ -104,4 +104,8 @@ void test() {
ts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+
+#if TEST_STD_VER >= 23
+ ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
}
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
index 0747556905200..34df0f1d44335 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
@@ -81,6 +81,10 @@ void test() {
ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+#if TEST_STD_VER >= 23
+ tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
m[key]; // no-warning
m[std::move(key)]; // no-warning
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
index cdb47d2b514ab..9ce6bb6a5d46c 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
@@ -81,6 +81,10 @@ void test() {
ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+#if TEST_STD_VER >= 23
+ tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
m.bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
m.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
index ff62874caf01b..dbb10eaa40dd3 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
@@ -83,6 +83,10 @@ void test() {
ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+#if TEST_STD_VER >= 23
+ tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
us.bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
index 628c31a93099f..d289d0ebfbfd0 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
@@ -82,6 +82,10 @@ void test() {
ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+#if TEST_STD_VER >= 23
+ tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
+
us.bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
>From 06e91a61500d7ded779eecfbdb0e31341837c63a Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 18:55:54 +0300
Subject: [PATCH 08/21] Refactor nodiscard tests
---
.../associative/map/map.nodiscard.verify.cpp | 18 +++++++++---------
.../map/multimap.nodiscard.verify.cpp | 18 +++++++++---------
.../set/multiset.nodiscard.verify.cpp | 18 +++++++++---------
.../associative/set/set.nodiscard.verify.cpp | 18 +++++++++---------
.../unord/unord.map/nodiscard.verify.cpp | 18 +++++++++---------
.../unord/unord.multimap/nodiscard.verify.cpp | 18 +++++++++---------
.../unord/unord.multiset/nodiscard.verify.cpp | 18 +++++++++---------
.../unord/unord.set/nodiscard.verify.cpp | 17 +++++++++--------
8 files changed, 72 insertions(+), 71 deletions(-)
diff --git a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
index 7a418502a722b..c21456965190b 100644
--- a/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/map.nodiscard.verify.cpp
@@ -74,19 +74,23 @@ void test() {
m.key_comp(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
m.value_comp(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#if TEST_STD_VER >= 14
+ std::map<int, int, TransparentCompare> tm;
+ const std::map<int, int, TransparentCompare> ctm{};
+ TransparentKey tkey;
+#endif
+
#if TEST_STD_VER >= 17
m.extract(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
m.extract(m.cend()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+#if TEST_STD_VER >= 23
+ tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
m.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#if TEST_STD_VER >= 14
- std::map<int, int, TransparentCompare> tm;
- const std::map<int, int, TransparentCompare> ctm{};
-
- TransparentKey tkey;
-
tm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
@@ -121,8 +125,4 @@ void test() {
tm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
-
-#if TEST_STD_VER >= 23
- tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
}
diff --git a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
index 49ac8c6a76064..bb38153b22a8e 100644
--- a/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/map/multimap.nodiscard.verify.cpp
@@ -59,19 +59,23 @@ void test() {
int key = 0;
+#if TEST_STD_VER >= 14
+ std::multimap<int, int, TransparentCompare> tm;
+ const std::multimap<int, int, TransparentCompare> ctm{};
+ TransparentKey tkey;
+#endif
+
#if TEST_STD_VER >= 17
m.extract(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
m.extract(m.cend()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+#if TEST_STD_VER >= 23
+ tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
m.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#if TEST_STD_VER >= 14
- std::multimap<int, int, TransparentCompare> tm;
- const std::multimap<int, int, TransparentCompare> ctm{};
-
- TransparentKey tkey;
-
tm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
@@ -106,8 +110,4 @@ void test() {
tm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
-
-#if TEST_STD_VER >= 23
- tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
}
diff --git a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
index a90f69fc96af4..3a762b059e89a 100644
--- a/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/multiset.nodiscard.verify.cpp
@@ -53,10 +53,19 @@ void test() {
int key = 0;
+#if TEST_STD_VER >= 14
+ std::multiset<int, TransparentCompare> ts;
+ const std::multiset<int, TransparentCompare> cts{};
+ TransparentKey tkey;
+#endif
+
#if TEST_STD_VER >= 17
s.extract(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
s.extract(s.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+#if TEST_STD_VER >= 23
+ ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
s.get_allocator(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
s.key_comp(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -65,11 +74,6 @@ void test() {
s.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cs.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#if TEST_STD_VER >= 14
- std::multiset<int, TransparentCompare> ts;
- const std::multiset<int, TransparentCompare> cts{};
-
- TransparentKey tkey;
-
ts.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cts.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
@@ -104,8 +108,4 @@ void test() {
ts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
-
-#if TEST_STD_VER >= 23
- ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
}
diff --git a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
index 02f2634363679..acee396bfbb99 100644
--- a/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/associative/set/set.nodiscard.verify.cpp
@@ -53,10 +53,19 @@ void test() {
int key = 0;
+#if TEST_STD_VER >= 14
+ std::set<int, TransparentCompare> ts;
+ const std::set<int, TransparentCompare> cts{};
+ TransparentKey tkey;
+#endif
+
#if TEST_STD_VER >= 17
s.extract(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
s.extract(s.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+#if TEST_STD_VER >= 23
+ ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
s.get_allocator(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
s.key_comp(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -65,11 +74,6 @@ void test() {
s.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cs.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#if TEST_STD_VER >= 14
- std::set<int, TransparentCompare> ts;
- const std::set<int, TransparentCompare> cts{};
-
- TransparentKey tkey;
-
ts.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cts.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
@@ -104,8 +108,4 @@ void test() {
ts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cts.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
-
-#if TEST_STD_VER >= 23
- ts.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
}
diff --git a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
index 34df0f1d44335..19e6764c1f064 100644
--- a/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.map/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
int key = 0;
+#if TEST_STD_VER >= 20
+ std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
+ const std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
+ TransparentKey tkey;
+#endif
+
#if TEST_STD_VER >= 17
m.extract(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
m.extract(m.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+#if TEST_STD_VER >= 23
+ tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
m.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
m.key_eq(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
m.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#if TEST_STD_VER >= 20
- std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
- const std::unordered_map<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
-
- TransparentKey tkey;
-
tm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
@@ -81,10 +85,6 @@ void test() {
ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
-#if TEST_STD_VER >= 23
- tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
m[key]; // no-warning
m[std::move(key)]; // no-warning
diff --git a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
index 9ce6bb6a5d46c..511d9e2062d48 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multimap/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
int key = 0;
+#if TEST_STD_VER >= 20
+ std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
+ const std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
+ TransparentKey tkey;
+#endif
+
#if TEST_STD_VER >= 17
m.extract(0); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
m.extract(m.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+#if TEST_STD_VER >= 23
+ tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
m.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
m.key_eq(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
m.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cm.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#if TEST_STD_VER >= 20
- std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> tm;
- const std::unordered_multimap<StoredKey, int, TransparentKeyHash, std::equal_to<>> ctm;
-
- TransparentKey tkey;
-
tm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
ctm.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
@@ -81,10 +85,6 @@ void test() {
ctm.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
-#if TEST_STD_VER >= 23
- tm.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
m.bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
m.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
index dbb10eaa40dd3..1f0e76a412017 100644
--- a/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.multiset/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
int key = 0;
+#if TEST_STD_VER >= 20
+ std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
+ const std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
+ TransparentKey tkey;
+#endif
+
#if TEST_STD_VER >= 17
us.extract(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
us.extract(us.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+#if TEST_STD_VER >= 23
+ tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
us.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
us.key_eq(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,11 +66,6 @@ void test() {
us.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cus.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#if TEST_STD_VER >= 20
- std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
- const std::unordered_multiset<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
-
- TransparentKey tkey;
-
tus.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
ctus.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
@@ -83,10 +87,6 @@ void test() {
ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
-#if TEST_STD_VER >= 23
- tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
us.bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
diff --git a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
index d289d0ebfbfd0..2a3aca497e8c4 100644
--- a/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
+++ b/libcxx/test/libcxx/containers/unord/unord.set/nodiscard.verify.cpp
@@ -46,10 +46,19 @@ void test() {
int key = 0;
+#if TEST_STD_VER >= 20
+ std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
+ const std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
+ TransparentKey tkey;
+#endif
+
#if TEST_STD_VER >= 17
us.extract(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
us.extract(us.begin()); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
+#if TEST_STD_VER >= 23
+ tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
+#endif
us.hash_function(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
us.key_eq(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
@@ -57,10 +66,6 @@ void test() {
us.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
cus.find(key); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#if TEST_STD_VER >= 20
- std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> tus;
- const std::unordered_set<StoredKey, TransparentKeyHash, std::equal_to<>> ctus;
- TransparentKey tkey;
-
tus.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
ctus.find(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
@@ -82,10 +87,6 @@ void test() {
ctus.equal_range(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
#endif
-#if TEST_STD_VER >= 23
- tus.extract(tkey); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
-#endif
-
us.bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
us.max_bucket_count(); // expected-warning {{ignoring return value of function declared with 'nodiscard' attribute}}
>From 04f692b0cd29ee25babab4f9afd97c02fb56658d Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 19:02:05 +0300
Subject: [PATCH 09/21] Drop reformatting
---
libcxx/include/__tree | 2 +-
.../test/support/test_transparent_unordered.h | 46 +++++++++++--------
2 files changed, 28 insertions(+), 20 deletions(-)
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 0696a387bdfe2..454d8a8675c41 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -2089,7 +2089,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(_NodeHandle&& __n
if (__nh.empty())
return _InsertReturnType{end(), false, _NodeHandle()};
- __node_pointer __ptr = __nh.__ptr_;
+ __node_pointer __ptr = __nh.__ptr_;
auto [__parent, __child] = __find_equal(__ptr->__get_value());
if (__child != nullptr)
return _InsertReturnType{
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index f2439be716d44..5aee507191351 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -46,7 +46,7 @@ struct transparent_equal_final final : std::equal_to<> {};
template <typename T>
struct SearchedType {
- explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+ explicit SearchedType(T value, int *counter) : value_(value), conversions_(counter) { }
// Whenever a conversion is performed, increment the counter to keep track
// of conversions.
@@ -55,40 +55,48 @@ struct SearchedType {
return StoredType<T>{value_};
}
- T get_value() const { return value_; }
+ int get_value() const {
+ return value_;
+ }
private:
T value_;
- int* conversions_;
+ int *conversions_;
};
template <typename T>
struct StoredType {
StoredType() = default;
- StoredType(T value) : value_(value) {}
+ StoredType(T value) : value_(value) { }
- friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+ friend bool operator==(StoredType const& lhs, StoredType const& rhs) {
+ return lhs.value_ == rhs.value_;
+ }
// If we're being passed a SearchedType<T> object, avoid the conversion
// to T. This allows testing that the transparent operations are correctly
// forwarding the SearchedType all the way to this comparison by checking
// that we didn't have a conversion when we search for a SearchedType<T>
// in a container full of StoredType<T>.
- friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+ friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
+ return lhs.value_ == rhs.get_value();
+ }
- T get_value() const { return value_; }
+ int get_value() const {
+ return value_;
+ }
private:
T value_;
};
-template <template <class...> class UnorderedSet, class Hash, class Equal>
+template<template<class...> class UnorderedSet, class Hash, class Equal>
using unord_set_type = UnorderedSet<StoredType<int>, Hash, Equal>;
-template <template <class...> class UnorderedMap, class Hash, class Equal>
+template<template<class...> class UnorderedMap, class Hash, class Equal>
using unord_map_type = UnorderedMap<StoredType<int>, int, Hash, Equal>;
-template <class Container>
+template<class Container>
void test_transparent_find(Container c) {
int conversions = 0;
assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -97,7 +105,7 @@ void test_transparent_find(Container c) {
assert(conversions == 0);
}
-template <class Container>
+template<class Container>
void test_non_transparent_find(Container c) {
int conversions = 0;
assert(c.find(SearchedType<int>(1, &conversions)) != c.end());
@@ -108,7 +116,7 @@ void test_non_transparent_find(Container c) {
assert(conversions == 3);
}
-template <class Container>
+template<class Container>
void test_transparent_count(Container c) {
int conversions = 0;
assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -117,7 +125,7 @@ void test_transparent_count(Container c) {
assert(conversions == 0);
}
-template <class Container>
+template<class Container>
void test_non_transparent_count(Container c) {
int conversions = 0;
assert(c.count(SearchedType<int>(1, &conversions)) > 0);
@@ -128,7 +136,7 @@ void test_non_transparent_count(Container c) {
assert(conversions == 3);
}
-template <class Container>
+template<class Container>
void test_transparent_contains(Container c) {
int conversions = 0;
assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -137,7 +145,7 @@ void test_transparent_contains(Container c) {
assert(conversions == 0);
}
-template <class Container>
+template<class Container>
void test_non_transparent_contains(Container c) {
int conversions = 0;
assert(c.contains(SearchedType<int>(1, &conversions)));
@@ -148,10 +156,10 @@ void test_non_transparent_contains(Container c) {
assert(conversions == 3);
}
-template <class Container>
+template<class Container>
void test_transparent_equal_range(Container c) {
int conversions = 0;
- auto iters = c.equal_range(SearchedType<int>(1, &conversions));
+ auto iters = c.equal_range(SearchedType<int>(1, &conversions));
assert(std::distance(iters.first, iters.second) > 0);
iters = c.equal_range(SearchedType<int>(2, &conversions));
assert(std::distance(iters.first, iters.second) > 0);
@@ -160,10 +168,10 @@ void test_transparent_equal_range(Container c) {
assert(conversions == 0);
}
-template <class Container>
+template<class Container>
void test_non_transparent_equal_range(Container c) {
int conversions = 0;
- auto iters = c.equal_range(SearchedType<int>(1, &conversions));
+ auto iters = c.equal_range(SearchedType<int>(1, &conversions));
assert(std::distance(iters.first, iters.second) > 0);
assert(conversions == 1);
iters = c.equal_range(SearchedType<int>(2, &conversions));
>From 8e87adb5ca5b3c29c1e16ceec6f2e0dba0e8b254 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 19:07:29 +0300
Subject: [PATCH 10/21] typename ==> class
---
libcxx/include/map | 2 +-
libcxx/include/set | 4 ++--
libcxx/include/unordered_map | 4 ++--
libcxx/include/unordered_set | 2 +-
4 files changed, 6 insertions(+), 6 deletions(-)
diff --git a/libcxx/include/map b/libcxx/include/map
index 2266dbaabca9f..342d3443051f5 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -432,7 +432,7 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
- template <typename K>
+ template <class K>
node_type extract(K&& k); // C++23
iterator insert(node_type&& nh); // C++17
diff --git a/libcxx/include/set b/libcxx/include/set
index 62c4f2bbb59e2..81e6c68371d6e 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -125,7 +125,7 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
- template <typename K>
+ template <class K>
node_type extract(K&& k); // C++23
insert_return_type insert(node_type&& nh); // C++17
@@ -376,7 +376,7 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
- template <typename K>
+ template <class K>
node_type extract(K&& k); // C++23
iterator insert(node_type&& nh); // C++17
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index ca2173def1f71..da31c801d3d0c 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -139,7 +139,7 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
- template <typename K>
+ template <class K>
node_type extract(K&& k); // C++23
insert_return_type insert(node_type&& nh); // C++17
@@ -431,7 +431,7 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
- template <typename K>
+ template <class K>
node_type extract(K&& k); // C++23
iterator insert(node_type&& nh); // C++17
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 32a10c1be1e95..2ae6d02556f0d 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -131,7 +131,7 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
- template <typename K>
+ template <class K>
node_type extract(K&& k); // C++23
insert_return_type insert(node_type&& nh); // C++17
>From 4f75902a0d5a2b31b02db9fc7036fe4c38f2b86b Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 20:11:11 +0300
Subject: [PATCH 11/21] Better extract testing
---
.../support/test_transparent_associative.hpp | 56 ++++++++++++++-----
.../test/support/test_transparent_unordered.h | 50 ++++++++++++-----
2 files changed, 77 insertions(+), 29 deletions(-)
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index 6ec13d29000ea..2448f3cd16083 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -41,6 +41,10 @@ struct StoredType {
StoredType() = default;
StoredType(T value) : value_(value) {}
+ friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+
+ friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+
T get_value() const { return value_; }
auto operator<=>(const StoredType<T>&) const = default;
@@ -72,17 +76,17 @@ struct transparent_comparator_final final : public transparent_comparator_base {
template <class Container>
void test_transparent_erase(Container c) {
+ static_assert(
+ std::same_as<
+ typename Container::size_type,
+ std::invoke_result_t<decltype(&Container::template erase<SearchedType<int>>), Container, SearchedType<int>>>);
+
int conversions = 0;
+
assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
-
assert(conversions == 0);
-
- c.erase(c.begin());
- c.erase(c.cbegin());
-
- assert(c.empty());
}
template <class Container>
@@ -96,25 +100,47 @@ void test_non_transparent_erase(Container c) {
assert(conversions == 3);
}
+template <typename NodeHandle>
+concept node_handle_has_key = requires(NodeHandle nh) {
+ { nh.key() };
+};
+
+template <class T, class Container>
+void test_single_extract(SearchedType<T> key, Container& c) {
+ auto node_handle = c.extract(key);
+
+ assert(!node_handle.empty());
+
+ if constexpr (node_handle_has_key<typename Container::node_type>) {
+ assert(node_handle.key() == key);
+ } else {
+ assert(node_handle.value() == key);
+ }
+}
+
template <class Container>
void test_transparent_extract(Container c) {
+ static_assert(std::same_as< typename Container::node_type,
+ std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
+ Container,
+ SearchedType<int>>>);
+
int conversions = 0;
- assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
- assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+
+ test_single_extract(SearchedType<int>(1, &conversions), c);
+ test_single_extract(SearchedType<int>(2, &conversions), c);
+
assert(c.extract(SearchedType<int>(3, &conversions)).empty());
assert(conversions == 0);
-
- assert(!c.extract(c.cbegin()).empty());
- assert(c.empty());
}
template <class Container>
void test_non_transparent_extract(Container c) {
int conversions = 0;
- assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
- assert(conversions == 1);
- assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
- assert(conversions == 2);
+
+ test_single_extract(SearchedType<int>(1, &conversions), c);
+ test_single_extract(SearchedType<int>(2, &conversions), c);
+
assert(c.extract(SearchedType<int>(3, &conversions)).empty());
assert(conversions == 3);
}
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index 5aee507191351..1bc6fb07dee6f 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -186,17 +186,17 @@ void test_non_transparent_equal_range(Container c) {
template <class Container>
void test_transparent_erase(Container c) {
+ static_assert(
+ std::same_as<
+ typename Container::size_type,
+ std::invoke_result_t<decltype(&Container::template erase<SearchedType<int>>), Container, SearchedType<int>>>);
+
int conversions = 0;
assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
assert(c.erase(SearchedType<int>(2, &conversions)) != 0);
assert(c.erase(SearchedType<int>(3, &conversions)) == 0);
assert(conversions == 0);
-
- c.erase(c.begin());
- c.erase(c.cbegin());
-
- assert(c.empty());
}
template <class Container>
@@ -210,25 +210,47 @@ void test_non_transparent_erase(Container c) {
assert(conversions == 3);
}
+template <typename NodeHandle>
+concept node_handle_has_key = requires(NodeHandle nh) {
+ { nh.key() };
+};
+
+template <class T, class Container>
+void test_single_extract(SearchedType<T> key, Container& c) {
+ auto node_handle = c.extract(key);
+
+ assert(!node_handle.empty());
+
+ if constexpr (node_handle_has_key<typename Container::node_type>) {
+ assert(node_handle.key() == key);
+ } else {
+ assert(node_handle.value() == key);
+ }
+}
+
template <class Container>
void test_transparent_extract(Container c) {
+ static_assert(std::same_as< typename Container::node_type,
+ std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
+ Container,
+ SearchedType<int>>>);
+
int conversions = 0;
- assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
- assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
+
+ test_single_extract(SearchedType<int>(1, &conversions), c);
+ test_single_extract(SearchedType<int>(2, &conversions), c);
+
assert(c.extract(SearchedType<int>(3, &conversions)).empty());
assert(conversions == 0);
-
- assert(!c.extract(c.cbegin()).empty());
- assert(c.empty());
}
template <class Container>
void test_non_transparent_extract(Container c) {
int conversions = 0;
- assert(!c.extract(SearchedType<int>(1, &conversions)).empty());
- assert(conversions == 1);
- assert(!c.extract(SearchedType<int>(2, &conversions)).empty());
- assert(conversions == 2);
+
+ test_single_extract(SearchedType<int>(1, &conversions), c);
+ test_single_extract(SearchedType<int>(2, &conversions), c);
+
assert(c.extract(SearchedType<int>(3, &conversions)).empty());
assert(conversions == 3);
}
>From 2ccde5a7e6d22a2a6f3fdd1555a5785ec61a4cfa Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 20:43:59 +0300
Subject: [PATCH 12/21] Include concepts in test
---
libcxx/test/support/test_transparent_associative.hpp | 2 ++
1 file changed, 2 insertions(+)
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index 2448f3cd16083..dae3acc725162 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -15,6 +15,8 @@
#if TEST_STD_VER >= 23
+# include <concepts>
+
template <typename T>
struct StoredType;
>From 0e2f51800a6901355e730e2a00490100470ae709 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 9 Jan 2026 21:04:33 +0300
Subject: [PATCH 13/21] Include type_traits in test
---
libcxx/test/support/test_transparent_associative.hpp | 1 +
libcxx/test/support/test_transparent_unordered.h | 3 +++
2 files changed, 4 insertions(+)
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.hpp
index dae3acc725162..e3cac5e8a51cb 100644
--- a/libcxx/test/support/test_transparent_associative.hpp
+++ b/libcxx/test/support/test_transparent_associative.hpp
@@ -16,6 +16,7 @@
#if TEST_STD_VER >= 23
# include <concepts>
+# include <type_traits>
template <typename T>
struct StoredType;
diff --git a/libcxx/test/support/test_transparent_unordered.h b/libcxx/test/support/test_transparent_unordered.h
index 1bc6fb07dee6f..14faea4d8e5b7 100644
--- a/libcxx/test/support/test_transparent_unordered.h
+++ b/libcxx/test/support/test_transparent_unordered.h
@@ -16,6 +16,9 @@
#if TEST_STD_VER > 17
+# include <concepts>
+# include <type_traits>
+
template <typename T>
struct StoredType;
>From a671533b50ca7984ba4b06380af7670a24dc8276 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 10 Jan 2026 02:07:55 +0300
Subject: [PATCH 14/21] Correct header suffix
---
.../associative/map/map.erasure/erase.transparent.pass.cpp | 4 ++--
.../map/map.modifiers/extract.transparent.pass.cpp | 4 ++--
.../multimap/multimap.erasure/erase.transparent.pass.cpp | 4 ++--
.../multimap/multimap.modifiers/extract.transparent.pass.cpp | 4 ++--
.../associative/multiset/extract.transparent.pass.cpp | 4 ++--
.../multiset/multiset.erasure/erase.transparent.pass.cpp | 4 ++--
.../containers/associative/set/extract.transparent.pass.cpp | 4 ++--
.../set/{ => set.erasure}/erase.transparent.pass.cpp | 4 ++--
.../std/containers/unord/unord.map/erase.transparent.pass.cpp | 2 +-
.../unord.map.modifiers/extract.transparent.pass.cpp | 2 +-
.../unord/unord.multimap/erase.transparent.pass.cpp | 2 +-
.../unord.multimap.modifiers/extract.transparent.pass.cpp | 2 +-
.../unord/unord.multiset/erase.transparent.pass.cpp | 2 +-
.../unord/unord.multiset/extract.transparent.pass.cpp | 2 +-
.../std/containers/unord/unord.set/erase.transparent.pass.cpp | 2 +-
.../containers/unord/unord.set/extract.transparent.pass.cpp | 2 +-
...sparent_associative.hpp => test_transparent_associative.h} | 0
17 files changed, 24 insertions(+), 24 deletions(-)
rename libcxx/test/std/containers/associative/set/{ => set.erasure}/erase.transparent.pass.cpp (91%)
rename libcxx/test/support/{test_transparent_associative.hpp => test_transparent_associative.h} (100%)
diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
index 2fe83c20acf67..c8cfb77069d62 100644
--- a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
// class map
-// template<typename K>
+// template<class K>
// size_type erase(K&& k) const; // C++23
#include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
int main(int, char**) {
test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
index 0dd4d9afdeb9b..97cdcda5e519e 100644
--- a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
// class map
-// template<typename K>
+// template<class K>
// node_type extract(K&& k) const; // C++23
#include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
int main(int, char**) {
test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
index 7e3f625370d73..9a257b82b9388 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
// class multimap
-// template<typename K>
+// template<class K>
// size_type erase(K&& k) const; // C++23
#include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
int main(int, char**) {
test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
index 9faa74f69e2b0..f467d28996ed7 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
// class multimap
-// template<typename K>
+// template<class K>
// node_type extract(K&& k) const; // C++23
#include <map>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
int main(int, char**) {
test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
diff --git a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
index fc04c8d85c267..f05905e7483a1 100644
--- a/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
// class multiset
-// template<typename K>
+// template<class K>
// node_type extract(K&& k) const; // C++23
#include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
int main(int, char**) {
test_transparent_extract<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
diff --git a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
index a7252f0aa039c..2669b07e8fd8d 100644
--- a/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multiset/multiset.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
// class multiset
-// template<typename K>
+// template<class K>
// size_type erase(K&& k) const; // C++23
#include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
int main(int, char**) {
test_transparent_erase<std::multiset<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
diff --git a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
index 62c72808b83d1..0b036971e119e 100644
--- a/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/extract.transparent.pass.cpp
@@ -12,11 +12,11 @@
// class set
-// template<typename K>
+// template<class K>
// node_type extract(K&& k) const; // C++23
#include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
int main(int, char**) {
test_transparent_extract<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4});
diff --git a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
similarity index 91%
rename from libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
rename to libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
index d2ca1fe599374..87e99da2a9bef 100644
--- a/libcxx/test/std/containers/associative/set/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/set/set.erasure/erase.transparent.pass.cpp
@@ -12,11 +12,11 @@
// class set
-// template<typename K>
+// template<class K>
// size_type erase(K&& k) const; // C++23
#include <set>
-#include "test_transparent_associative.hpp"
+#include "test_transparent_associative.h"
int main(int, char**) {
test_transparent_erase<std::set<StoredType<int>, transparent_comparator_base>>({1, 2, 4, 5});
diff --git a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
index 2d0138ef6472f..edbaec04d3329 100644
--- a/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
// class unordered_map
-// template <typename K>
+// template <class K>
// size_type erase(K&& k);
#include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
index 08a2e05e777bc..a14dca09159ac 100644
--- a/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.map/unord.map.modifiers/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
// class unordered_map
-// template <typename K>
+// template <class K>
// node_type extract(K&& k);
#include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
index b15d915a618d9..e09b66da0ea10 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
// class unordered_multimap
-// template <typename K>
+// template <class K>
// size_type erase(K&& k);
#include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
index 8eefc2b5f689b..4af37297ec0ae 100644
--- a/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multimap/unord.multimap.modifiers/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
// class unordered_multimap
-// template <typename K>
+// template <class K>
// node_type extract(K&& k);
#include <unordered_map>
diff --git a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
index 41420d41613b1..3fa33f111b1c8 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
// class unordered_multiset
-// template <typename K>
+// template <class K>
// size_type erase(K&& k);
#include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
index e094201a8cedd..9ec51b55d2278 100644
--- a/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.multiset/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
// class unordered_multiset
-// template <typename K>
+// template <class K>
// node_type extract(K&& k);
#include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
index efc55a0ea8964..944faf091d5df 100644
--- a/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/erase.transparent.pass.cpp
@@ -12,7 +12,7 @@
// class unordered_set
-// template <typename K>
+// template <class K>
// size_type erase(K&& k);
#include <unordered_set>
diff --git a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
index e84facdaabcb0..06ad1deaf05d1 100644
--- a/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/unord/unord.set/extract.transparent.pass.cpp
@@ -12,7 +12,7 @@
// class unordered_set
-// template <typename K>
+// template <class K>
// node_type extract(K&& k);
#include <unordered_set>
diff --git a/libcxx/test/support/test_transparent_associative.hpp b/libcxx/test/support/test_transparent_associative.h
similarity index 100%
rename from libcxx/test/support/test_transparent_associative.hpp
rename to libcxx/test/support/test_transparent_associative.h
>From 81941a6b5226fc1a8be0a21829b12394204a3efb Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 10 Jan 2026 02:15:39 +0300
Subject: [PATCH 15/21] typename ==> class
---
libcxx/include/map | 2 +-
libcxx/include/set | 4 ++--
libcxx/include/unordered_map | 4 ++--
libcxx/include/unordered_set | 6 +++---
4 files changed, 8 insertions(+), 8 deletions(-)
diff --git a/libcxx/include/map b/libcxx/include/map
index 342d3443051f5..12a783df8fc4d 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -441,7 +441,7 @@ public:
iterator erase(const_iterator position);
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
- template <typename K>
+ template <class K>
size_type erase(K&& k); // C++23
iterator erase(const_iterator first, const_iterator last);
diff --git a/libcxx/include/set b/libcxx/include/set
index 81e6c68371d6e..acbe0bf9307ae 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -134,7 +134,7 @@ public:
iterator erase(const_iterator position);
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
- template <typename K>
+ template <class K>
size_type erase(K&& k); // C++23
iterator erase(const_iterator first, const_iterator last);
@@ -386,7 +386,7 @@ public:
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
iterator erase(const_iterator first, const_iterator last);
- template <typename K>
+ template <class K>
size_type erase(K&& k); // C++23
void clear() noexcept;
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index da31c801d3d0c..f81270797228f 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -165,7 +165,7 @@ public:
iterator erase(const_iterator position);
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
- template <typename K>
+ template <class K>
size_type erase(K&& k); // C++23
iterator erase(const_iterator first, const_iterator last);
@@ -440,7 +440,7 @@ public:
iterator erase(const_iterator position);
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
- template <typename K>
+ template <class K>
size_type erase(K&& k); // C++23
iterator erase(const_iterator first, const_iterator last);
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index 2ae6d02556f0d..c0636fe437698 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -140,7 +140,7 @@ public:
iterator erase(const_iterator position);
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
- template <typename K>
+ template <class K>
size_type erase(K&& k); // C++23
iterator erase(const_iterator first, const_iterator last);
@@ -385,7 +385,7 @@ public:
node_type extract(const_iterator position); // C++17
node_type extract(const key_type& x); // C++17
- template <typename K>
+ template <class K>
node_type extract(K&& k); // C++23
iterator insert(node_type&& nh); // C++17
@@ -394,7 +394,7 @@ public:
iterator erase(const_iterator position);
iterator erase(iterator position); // C++14
size_type erase(const key_type& k);
- template <typename K>
+ template <class K>
size_type erase(K&& k); // C++23
iterator erase(const_iterator first, const_iterator last);
>From fbfe885e023aeab62b3efd960523c70da6124c37 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Sat, 7 Feb 2026 19:04:30 +0300
Subject: [PATCH 16/21] Rename extract method
---
libcxx/include/__hash_table | 6 +++---
libcxx/include/__tree | 6 +++---
libcxx/include/map | 4 ++--
libcxx/include/set | 4 ++--
libcxx/include/unordered_map | 4 ++--
libcxx/include/unordered_set | 4 ++--
6 files changed, 14 insertions(+), 14 deletions(-)
diff --git a/libcxx/include/__hash_table b/libcxx/include/__hash_table
index ff3d0f1ab7749..19ecb90928c7f 100644
--- a/libcxx/include/__hash_table
+++ b/libcxx/include/__hash_table
@@ -899,7 +899,7 @@ public:
template <class _NodeHandle, class _Key>
_LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract(const _Key& __key);
template <class _NodeHandle>
- _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_iterator(const_iterator __it);
+ _LIBCPP_HIDE_FROM_ABI _NodeHandle __node_handle_extract_from_iterator(const_iterator __it);
#endif
_LIBCPP_HIDE_FROM_ABI void clear() _NOEXCEPT;
@@ -1653,13 +1653,13 @@ _LIBCPP_HIDE_FROM_ABI _NodeHandle __hash_table<_Tp, _Hash, _Equal, _Alloc>::__no
iterator __i = find(__key);
if (__i == end())
return _NodeHandle();
- return __node_handle_extract_iterator<_NodeHandle>(__i);
+ return __node_handle_extract_from_iterator<_NodeHandle>(__i);
}
template <class _Tp, class _Hash, class _Equal, class _Alloc>
template <class _NodeHandle>
_LIBCPP_HIDE_FROM_ABI _NodeHandle
-__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract_iterator(const_iterator __p) {
+__hash_table<_Tp, _Hash, _Equal, _Alloc>::__node_handle_extract_from_iterator(const_iterator __p) {
allocator_type __alloc(__node_alloc());
return _NodeHandle(remove(__p).release(), __alloc);
}
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 454d8a8675c41..5b49308d7746f 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -1242,7 +1242,7 @@ public:
template <class _NodeHandle, class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract(const _Key&);
template <class _NodeHandle>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract_iterator(const_iterator);
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle __node_handle_extract_from_iterator(const_iterator);
#endif
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 iterator erase(const_iterator __p);
@@ -2126,13 +2126,13 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const& _Key __key) {
iterator __it = __lower_bound_multi(__key);
if (__it == end() || __value_comp_(__key, *__it))
return _NodeHandle();
- return __node_handle_extract_iterator<_NodeHandle>(__it);
+ return __node_handle_extract_from_iterator<_NodeHandle>(__it);
}
template <class _Tp, class _Compare, class _Allocator>
template <class _NodeHandle>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract_iterator(const_iterator __p) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract_from_iterator(const_iterator __p) {
__node_pointer __np = __p.__get_np();
__remove_node_pointer(__np);
return _NodeHandle(__np, __alloc());
diff --git a/libcxx/include/map b/libcxx/include/map
index 12a783df8fc4d..ab5c0c9b5840d 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1377,7 +1377,7 @@ public:
return __tree_.template __node_handle_extract<node_type>(__key);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
- return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+ return __tree_.template __node_handle_extract_iterator_from_iterator<node_type>(__it.__i_);
}
# if _LIBCPP_STD_VER >= 23
@@ -2070,7 +2070,7 @@ public:
return __tree_.template __node_handle_extract<node_type>(__key);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
- return __tree_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+ return __tree_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
}
# if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/set b/libcxx/include/set
index acbe0bf9307ae..bf64853131e8f 100644
--- a/libcxx/include/set
+++ b/libcxx/include/set
@@ -811,7 +811,7 @@ public:
return __tree_.template __node_handle_extract<node_type>(__key);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
- return __tree_.template __node_handle_extract_iterator<node_type>(__it);
+ return __tree_.template __node_handle_extract_from_iterator<node_type>(__it);
}
# if _LIBCPP_STD_VER >= 23
@@ -1324,7 +1324,7 @@ public:
return __tree_.template __node_handle_extract<node_type>(__key);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
- return __tree_.template __node_handle_extract_iterator<node_type>(__it);
+ return __tree_.template __node_handle_extract_from_iterator<node_type>(__it);
}
# if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/unordered_map b/libcxx/include/unordered_map
index f81270797228f..41784958719aa 100644
--- a/libcxx/include/unordered_map
+++ b/libcxx/include/unordered_map
@@ -1199,7 +1199,7 @@ public:
return __table_.template __node_handle_extract<node_type>(__key);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
- return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+ return __table_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
}
# if _LIBCPP_STD_VER >= 23
@@ -1960,7 +1960,7 @@ public:
return __table_.template __node_handle_extract<node_type>(__key);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
- return __table_.template __node_handle_extract_iterator<node_type>(__it.__i_);
+ return __table_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
}
# if _LIBCPP_STD_VER >= 23
diff --git a/libcxx/include/unordered_set b/libcxx/include/unordered_set
index c0636fe437698..50be2c8c6222d 100644
--- a/libcxx/include/unordered_set
+++ b/libcxx/include/unordered_set
@@ -829,7 +829,7 @@ public:
return __table_.template __node_handle_extract<node_type>(__key);
}
[[nodiscard]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
- return __table_.template __node_handle_extract_iterator<node_type>(__it);
+ return __table_.template __node_handle_extract_from_iterator<node_type>(__it);
}
# if _LIBCPP_STD_VER >= 23
@@ -1447,7 +1447,7 @@ public:
return __table_.template __node_handle_insert_multi<node_type>(__hint, std::move(__nh));
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __position) {
- return __table_.template __node_handle_extract_iterator<node_type>(__position);
+ return __table_.template __node_handle_extract_from_iterator<node_type>(__position);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(key_type const& __key) {
return __table_.template __node_handle_extract<node_type>(__key);
>From 7e10521630b8e832883099517dc62ef8ece7725e Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Tue, 16 Jun 2026 04:22:05 +0300
Subject: [PATCH 17/21] fixes after rebase
---
libcxx/include/__tree | 2 +-
libcxx/include/map | 9 +++++----
2 files changed, 6 insertions(+), 5 deletions(-)
diff --git a/libcxx/include/__tree b/libcxx/include/__tree
index 5b49308d7746f..50b92bc3a8b44 100644
--- a/libcxx/include/__tree
+++ b/libcxx/include/__tree
@@ -2122,7 +2122,7 @@ __tree<_Tp, _Compare, _Allocator>::__node_handle_insert_unique(const_iterator __
template <class _Tp, class _Compare, class _Allocator>
template <class _NodeHandle, class _Key>
_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 _NodeHandle
-__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const& _Key __key) {
+__tree<_Tp, _Compare, _Allocator>::__node_handle_extract(const _Key& __key) {
iterator __it = __lower_bound_multi(__key);
if (__it == end() || __value_comp_(__key, *__it))
return _NodeHandle();
diff --git a/libcxx/include/map b/libcxx/include/map
index ab5c0c9b5840d..71f0284a55cee 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -147,7 +147,7 @@ public:
constexpr node_type extract(const_iterator position); // C++17
constexpr node_type extract(const key_type& x); // C++17
- template <typename K>
+ template <class K>
constexpr node_type extract(K&& k); // C++23
constexpr insert_return_type insert(node_type&& nh); // C++17
@@ -173,7 +173,7 @@ public:
constexpr iterator erase(const_iterator position);
constexpr iterator erase(iterator position); // C++14
constexpr size_type erase(const key_type& k);
- template <typename K>
+ template <class K>
constexpr size_type erase(K&& k); // C++23
constexpr iterator erase(const_iterator first, const_iterator last);
@@ -1049,7 +1049,8 @@ public:
# if _LIBCPP_STD_VER >= 14
template <class _InputIterator>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+ _LIBCPP_HIDE_FROM_ABI
+ _LIBCPP_CONSTEXPR_SINCE_CXX26
map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
: map(__f, __l, key_compare(), __a) {}
# endif
@@ -1377,7 +1378,7 @@ public:
return __tree_.template __node_handle_extract<node_type>(__key);
}
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
- return __tree_.template __node_handle_extract_iterator_from_iterator<node_type>(__it.__i_);
+ return __tree_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
}
# if _LIBCPP_STD_VER >= 23
>From d68653145d05bcd2351fa73a7fc203e5ec69f53b Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Tue, 16 Jun 2026 04:30:02 +0300
Subject: [PATCH 18/21] release 23
---
libcxx/docs/ReleaseNotes/22.rst | 1 -
libcxx/docs/ReleaseNotes/23.rst | 1 +
libcxx/docs/Status/Cxx23Papers.csv | 2 +-
libcxx/include/map | 3 +--
4 files changed, 3 insertions(+), 4 deletions(-)
diff --git a/libcxx/docs/ReleaseNotes/22.rst b/libcxx/docs/ReleaseNotes/22.rst
index 5b05666cbb419..9f9927095a61f 100644
--- a/libcxx/docs/ReleaseNotes/22.rst
+++ b/libcxx/docs/ReleaseNotes/22.rst
@@ -38,7 +38,6 @@ What's New in Libc++ 22.0.0?
Implemented Papers
------------------
-- P2077R3: Heterogeneous erasure overloads for associative containers (`Github <https://llvm.org/PR105165>`__)
- P2592R3: Hashing support for ``std::chrono`` value classes (`Github <https://llvm.org/PR105358>`__)
- P2321R2: ``zip`` (`Github <https://llvm.org/PR105169>`__)
- P2988R12: ``std::optional<T&>`` (`Github <https://llvm.org/PR148131>`__)
diff --git a/libcxx/docs/ReleaseNotes/23.rst b/libcxx/docs/ReleaseNotes/23.rst
index 37aa972d22ea3..804277820090c 100644
--- a/libcxx/docs/ReleaseNotes/23.rst
+++ b/libcxx/docs/ReleaseNotes/23.rst
@@ -55,6 +55,7 @@ Implemented Papers
- P3369R0: constexpr for ``uninitialized_default_construct`` (`Github <https://llvm.org/PR118380>`__)
- P3508R0: Wording for "constexpr for specialized memory algorithms" (`Github <https://llvm.org/PR118379>`__)
- P4206R0: Revert string support in ``std::constant_wrapper`` (`Github <https://llvm.org/PR203338>`__)
+- P2077R3: Heterogeneous erasure overloads for associative containers (`Github <https://llvm.org/PR105165>`__)
Improvements and New Features
-----------------------------
diff --git a/libcxx/docs/Status/Cxx23Papers.csv b/libcxx/docs/Status/Cxx23Papers.csv
index d6fa695026543..fac8f0061e305 100644
--- a/libcxx/docs/Status/Cxx23Papers.csv
+++ b/libcxx/docs/Status/Cxx23Papers.csv
@@ -30,7 +30,7 @@
"`P1147R1 <https://wg21.link/P1147R1>`__","Printing ``volatile`` Pointers","2021-10 (Virtual)","|Complete|","14","`#105161 <https://github.com/llvm/llvm-project/issues/105161>`__",""
"`P1272R4 <https://wg21.link/P1272R4>`__","Byteswapping for fun&&nuf","2021-10 (Virtual)","|Complete|","14","`#105163 <https://github.com/llvm/llvm-project/issues/105163>`__",""
"`P1675R2 <https://wg21.link/P1675R2>`__","``rethrow_exception`` must be allowed to copy","2021-10 (Virtual)","|Nothing To Do|","","`#105164 <https://github.com/llvm/llvm-project/issues/105164>`__",""
-"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","Complete","22","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
+"`P2077R3 <https://wg21.link/P2077R3>`__","Heterogeneous erasure overloads for associative containers","2021-10 (Virtual)","Complete","23","`#105165 <https://github.com/llvm/llvm-project/issues/105165>`__",""
"`P2251R1 <https://wg21.link/P2251R1>`__","Require ``span`` & ``basic_string_view`` to be Trivially Copyable","2021-10 (Virtual)","|Complete|","14","`#105166 <https://github.com/llvm/llvm-project/issues/105166>`__",""
"`P2301R1 <https://wg21.link/P2301R1>`__","Add a ``pmr`` alias for ``std::stacktrace``","2021-10 (Virtual)","","","`#105167 <https://github.com/llvm/llvm-project/issues/105167>`__",""
"`P2321R2 <https://wg21.link/P2321R2>`__","``zip``","2021-10 (Virtual)","|Complete|","22","`#105169 <https://github.com/llvm/llvm-project/issues/105169>`__",""
diff --git a/libcxx/include/map b/libcxx/include/map
index 71f0284a55cee..3ede95877bcb7 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1050,8 +1050,7 @@ public:
# if _LIBCPP_STD_VER >= 14
template <class _InputIterator>
_LIBCPP_HIDE_FROM_ABI
- _LIBCPP_CONSTEXPR_SINCE_CXX26
- map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+ _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
: map(__f, __l, key_compare(), __a) {}
# endif
>From 8199a11d4405a8ea72fd396cee53e1b04e8be4d5 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 26 Jun 2026 22:28:04 +0300
Subject: [PATCH 19/21] add constexpr tests for map and multimap
---
libcxx/include/__node_handle | 4 +--
libcxx/include/map | 18 +++++-----
.../map.erasure/erase.transparent.pass.cpp | 14 ++++++--
.../extract.transparent.pass.cpp | 14 ++++++--
.../erase.transparent.pass.cpp | 14 ++++++--
.../extract.transparent.pass.cpp | 14 ++++++--
.../support/test_transparent_associative.h | 35 ++++++++++---------
7 files changed, 77 insertions(+), 36 deletions(-)
diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index 789f2b5f9a52f..bb3313d6fcce6 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -178,11 +178,11 @@ struct __map_node_handle_specifics {
using mapped_type = typename _NodeType::__node_value_type::second_type;
// This method is not constexpr as per the standard.
- _LIBCPP_HIDE_FROM_ABI key_type& key() const {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_type& key() const {
return const_cast<key_type&>(static_cast<_Derived const*>(this)->__ptr_->__get_value().first);
}
- _LIBCPP_HIDE_FROM_ABI mapped_type& mapped() const {
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 mapped_type& mapped() const {
return static_cast<_Derived const*>(this)->__ptr_->__get_value().second;
}
};
diff --git a/libcxx/include/map b/libcxx/include/map
index 3ede95877bcb7..9b33ae697a001 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1049,8 +1049,8 @@ public:
# if _LIBCPP_STD_VER >= 14
template <class _InputIterator>
- _LIBCPP_HIDE_FROM_ABI
- _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+ map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
: map(__f, __l, key_compare(), __a) {}
# endif
@@ -1855,15 +1855,15 @@ public:
# if _LIBCPP_STD_VER >= 14
template <class _InputIterator>
- _LIBCPP_HIDE_FROM_ABI
- _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+ multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
: multimap(__f, __l, key_compare(), __a) {}
# endif
# if _LIBCPP_STD_VER >= 23
template <_ContainerCompatibleRange<value_type> _Range>
- _LIBCPP_HIDE_FROM_ABI
- _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(from_range_t, _Range&& __range, const allocator_type& __a)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+ multimap(from_range_t, _Range&& __range, const allocator_type& __a)
: multimap(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
# endif
@@ -1893,8 +1893,8 @@ public:
}
# if _LIBCPP_STD_VER >= 14
- _LIBCPP_HIDE_FROM_ABI
- _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(initializer_list<value_type> __il, const allocator_type& __a)
+ _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
+ multimap(initializer_list<value_type> __il, const allocator_type& __a)
: multimap(__il, key_compare(), __a) {}
# endif
@@ -2069,7 +2069,7 @@ public:
[[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(key_type const& __key) {
return __tree_.template __node_handle_extract<node_type>(__key);
}
- [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI node_type extract(const_iterator __it) {
+ [[__nodiscard__]] _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 node_type extract(const_iterator __it) {
return __tree_.template __node_handle_extract_from_iterator<node_type>(__it.__i_);
}
diff --git a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
index c8cfb77069d62..fbb632847b38f 100644
--- a/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.erasure/erase.transparent.pass.cpp
@@ -13,16 +13,26 @@
// class map
// template<class K>
-// size_type erase(K&& k) const; // C++23
+// constexpr size_type erase(K&& k) const; // C++23
#include <map>
#include "test_transparent_associative.h"
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}, {5, 0}});
test_transparent_erase<std::map<StoredType<int>, int, transparent_comparator_final>>(
{{1, 0}, {2, 0}, {4, 0}, {5, 0}});
test_non_transparent_erase<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if TEST_STD_VER >= 26
+ static_assert(test());
+#endif
+ return 0;
}
diff --git a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
index 97cdcda5e519e..01544585333cc 100644
--- a/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/map/map.modifiers/extract.transparent.pass.cpp
@@ -13,15 +13,25 @@
// class map
// template<class K>
-// node_type extract(K&& k) const; // C++23
+// constexpr node_type extract(K&& k) const; // C++23
#include <map>
#include "test_transparent_associative.h"
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
test_transparent_extract<std::map<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
test_non_transparent_extract<std::map<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if TEST_STD_VER >= 26
+ static_assert(test());
+#endif
+ return 0;
}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
index 9a257b82b9388..bba57e93c397b 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.erasure/erase.transparent.pass.cpp
@@ -13,12 +13,12 @@
// class multimap
// template<class K>
-// size_type erase(K&& k) const; // C++23
+// constexpr size_type erase(K&& k) const; // C++23
#include <map>
#include "test_transparent_associative.h"
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
test_transparent_erase<std::multimap<StoredType<int>, int, transparent_comparator_base>>(
{{1, 0}, {2, 0}, {4, 0}, {5, 0}});
@@ -26,4 +26,14 @@ int main(int, char**) {
{{1, 0}, {2, 0}, {4, 0}, {5, 0}});
test_non_transparent_erase<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if TEST_STD_VER >= 26
+ static_assert(test());
+#endif
+ return 0;
}
diff --git a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
index f467d28996ed7..0c54072aa94cd 100644
--- a/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
+++ b/libcxx/test/std/containers/associative/multimap/multimap.modifiers/extract.transparent.pass.cpp
@@ -13,15 +13,25 @@
// class multimap
// template<class K>
-// node_type extract(K&& k) const; // C++23
+// constexpr node_type extract(K&& k) const; // C++23
#include <map>
#include "test_transparent_associative.h"
-int main(int, char**) {
+TEST_CONSTEXPR_CXX26 bool test() {
test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_base>>({{1, 0}, {2, 0}, {4, 0}});
test_transparent_extract<std::multimap<StoredType<int>, int, transparent_comparator_final>>({{1, 0}, {2, 0}, {4, 0}});
test_non_transparent_extract<std::multimap<StoredType<int>, int, std::less<StoredType<int>>>>({{1, 0}, {2, 0}});
+
+ return true;
+}
+
+int main(int, char**) {
+ test();
+#if TEST_STD_VER >= 26
+ static_assert(test());
+#endif
+ return 0;
}
diff --git a/libcxx/test/support/test_transparent_associative.h b/libcxx/test/support/test_transparent_associative.h
index e3cac5e8a51cb..e8acb8d986a88 100644
--- a/libcxx/test/support/test_transparent_associative.h
+++ b/libcxx/test/support/test_transparent_associative.h
@@ -23,16 +23,16 @@ struct StoredType;
template <typename T>
struct SearchedType {
- explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
+ constexpr explicit SearchedType(T value, int* counter) : value_(value), conversions_(counter) {}
- operator StoredType<T>() const {
+ constexpr operator StoredType<T>() const {
++*conversions_;
return StoredType<T>{value_};
}
- T get_value() const { return value_; }
+ constexpr T get_value() const { return value_; }
- auto operator<=>(const SearchedType<T>&) const = default;
+ constexpr auto operator<=>(const SearchedType<T>&) const = default;
private:
T value_;
@@ -42,15 +42,16 @@ struct SearchedType {
template <typename T>
struct StoredType {
StoredType() = default;
- StoredType(T value) : value_(value) {}
- friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
+ constexpr StoredType(T value) : value_(value) {}
- friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+ constexpr friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
- T get_value() const { return value_; }
+ constexpr friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
- auto operator<=>(const StoredType<T>&) const = default;
+ constexpr T get_value() const { return value_; }
+
+ constexpr auto operator<=>(const StoredType<T>&) const = default;
private:
T value_;
@@ -60,17 +61,17 @@ struct transparent_comparator_base {
using is_transparent = void;
template <typename T>
- bool operator()(const SearchedType<T>& lhs, const StoredType<T>& rhs) const {
+ constexpr bool operator()(const SearchedType<T>& lhs, const StoredType<T>& rhs) const {
return lhs.get_value() < rhs.get_value();
}
template <typename T>
- bool operator()(const StoredType<T>& lhs, const SearchedType<T>& rhs) const {
+ constexpr bool operator()(const StoredType<T>& lhs, const SearchedType<T>& rhs) const {
return lhs.get_value() < rhs.get_value();
}
template <typename T>
- bool operator()(const StoredType<T>& lhs, const StoredType<T>& rhs) const {
+ constexpr bool operator()(const StoredType<T>& lhs, const StoredType<T>& rhs) const {
return lhs < rhs;
}
};
@@ -78,7 +79,7 @@ struct transparent_comparator_base {
struct transparent_comparator_final final : public transparent_comparator_base {};
template <class Container>
-void test_transparent_erase(Container c) {
+constexpr void test_transparent_erase(Container c) {
static_assert(
std::same_as<
typename Container::size_type,
@@ -93,7 +94,7 @@ void test_transparent_erase(Container c) {
}
template <class Container>
-void test_non_transparent_erase(Container c) {
+constexpr void test_non_transparent_erase(Container c) {
int conversions = 0;
assert(c.erase(SearchedType<int>(1, &conversions)) != 0);
assert(conversions == 1);
@@ -109,7 +110,7 @@ concept node_handle_has_key = requires(NodeHandle nh) {
};
template <class T, class Container>
-void test_single_extract(SearchedType<T> key, Container& c) {
+constexpr void test_single_extract(SearchedType<T> key, Container& c) {
auto node_handle = c.extract(key);
assert(!node_handle.empty());
@@ -122,7 +123,7 @@ void test_single_extract(SearchedType<T> key, Container& c) {
}
template <class Container>
-void test_transparent_extract(Container c) {
+constexpr void test_transparent_extract(Container c) {
static_assert(std::same_as< typename Container::node_type,
std::invoke_result_t<decltype(&Container::template extract<SearchedType<int>>),
Container,
@@ -138,7 +139,7 @@ void test_transparent_extract(Container c) {
}
template <class Container>
-void test_non_transparent_extract(Container c) {
+constexpr void test_non_transparent_extract(Container c) {
int conversions = 0;
test_single_extract(SearchedType<int>(1, &conversions), c);
>From 36fe70a8a4285356536b844484e0ed5a1f54685d Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 26 Jun 2026 22:37:08 +0300
Subject: [PATCH 20/21] fix node_handle case
---
libcxx/include/__node_handle | 4 ++--
libcxx/test/support/test_transparent_associative.h | 11 -----------
2 files changed, 2 insertions(+), 13 deletions(-)
diff --git a/libcxx/include/__node_handle b/libcxx/include/__node_handle
index bb3313d6fcce6..789f2b5f9a52f 100644
--- a/libcxx/include/__node_handle
+++ b/libcxx/include/__node_handle
@@ -178,11 +178,11 @@ struct __map_node_handle_specifics {
using mapped_type = typename _NodeType::__node_value_type::second_type;
// This method is not constexpr as per the standard.
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 key_type& key() const {
+ _LIBCPP_HIDE_FROM_ABI key_type& key() const {
return const_cast<key_type&>(static_cast<_Derived const*>(this)->__ptr_->__get_value().first);
}
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26 mapped_type& mapped() const {
+ _LIBCPP_HIDE_FROM_ABI mapped_type& mapped() const {
return static_cast<_Derived const*>(this)->__ptr_->__get_value().second;
}
};
diff --git a/libcxx/test/support/test_transparent_associative.h b/libcxx/test/support/test_transparent_associative.h
index e8acb8d986a88..90eb2feae4e8e 100644
--- a/libcxx/test/support/test_transparent_associative.h
+++ b/libcxx/test/support/test_transparent_associative.h
@@ -104,22 +104,11 @@ constexpr void test_non_transparent_erase(Container c) {
assert(conversions == 3);
}
-template <typename NodeHandle>
-concept node_handle_has_key = requires(NodeHandle nh) {
- { nh.key() };
-};
-
template <class T, class Container>
constexpr void test_single_extract(SearchedType<T> key, Container& c) {
auto node_handle = c.extract(key);
assert(!node_handle.empty());
-
- if constexpr (node_handle_has_key<typename Container::node_type>) {
- assert(node_handle.key() == key);
- } else {
- assert(node_handle.value() == key);
- }
}
template <class Container>
>From eca5388fc4018b092c9de635b47b955469a9dd02 Mon Sep 17 00:00:00 2001
From: rsaddatimov <rafa.saddatimov at gmail.com>
Date: Fri, 26 Jun 2026 22:54:03 +0300
Subject: [PATCH 21/21] fix format
---
libcxx/include/map | 16 ++++++++--------
.../test/support/test_transparent_associative.h | 4 +++-
2 files changed, 11 insertions(+), 9 deletions(-)
diff --git a/libcxx/include/map b/libcxx/include/map
index 9b33ae697a001..e431c79479c10 100644
--- a/libcxx/include/map
+++ b/libcxx/include/map
@@ -1049,8 +1049,8 @@ public:
# if _LIBCPP_STD_VER >= 14
template <class _InputIterator>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
- map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+ _LIBCPP_HIDE_FROM_ABI
+ _LIBCPP_CONSTEXPR_SINCE_CXX26 map(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
: map(__f, __l, key_compare(), __a) {}
# endif
@@ -1855,15 +1855,15 @@ public:
# if _LIBCPP_STD_VER >= 14
template <class _InputIterator>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
- multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
+ _LIBCPP_HIDE_FROM_ABI
+ _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(_InputIterator __f, _InputIterator __l, const allocator_type& __a)
: multimap(__f, __l, key_compare(), __a) {}
# endif
# if _LIBCPP_STD_VER >= 23
template <_ContainerCompatibleRange<value_type> _Range>
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
- multimap(from_range_t, _Range&& __range, const allocator_type& __a)
+ _LIBCPP_HIDE_FROM_ABI
+ _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(from_range_t, _Range&& __range, const allocator_type& __a)
: multimap(from_range, std::forward<_Range>(__range), key_compare(), __a) {}
# endif
@@ -1893,8 +1893,8 @@ public:
}
# if _LIBCPP_STD_VER >= 14
- _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX26
- multimap(initializer_list<value_type> __il, const allocator_type& __a)
+ _LIBCPP_HIDE_FROM_ABI
+ _LIBCPP_CONSTEXPR_SINCE_CXX26 multimap(initializer_list<value_type> __il, const allocator_type& __a)
: multimap(__il, key_compare(), __a) {}
# endif
diff --git a/libcxx/test/support/test_transparent_associative.h b/libcxx/test/support/test_transparent_associative.h
index 90eb2feae4e8e..85c35e3acff8f 100644
--- a/libcxx/test/support/test_transparent_associative.h
+++ b/libcxx/test/support/test_transparent_associative.h
@@ -47,7 +47,9 @@ struct StoredType {
constexpr friend bool operator==(StoredType const& lhs, StoredType const& rhs) { return lhs.value_ == rhs.value_; }
- constexpr friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) { return lhs.value_ == rhs.get_value(); }
+ constexpr friend bool operator==(StoredType const& lhs, SearchedType<T> const& rhs) {
+ return lhs.value_ == rhs.get_value();
+ }
constexpr T get_value() const { return value_; }
More information about the libcxx-commits
mailing list