[Note: fails 23_containers/multiset/modifiers/emplace/92878_92947.cc
but with no error messages.]
Implements P2353R5 "Extending associative containers with the
remaining heterogeneous overloads". Adds overloads templated on
heterogeneous key types for several members of associative
containers, particularly insertions:
uno uno uno uno
set map set map mset mmap
+ + insert
+ + op[], at, try_emplace,
insert_or_assign
+ + + + bucket
The insert* and try_emplace members also get a hinted overload.
Nothing is added to the multiset or multimap tree containers.
The new overloads enforce concept __heterogeneous_tree_key or
__heterogeneous_hash_key, as in P2077, to enforce the function
objects provided meet requirements, and that the key supplied
is not an iterator or the native key. Insertions implicitly
construct the required key_type object from the argument.
Doxygen annotations are improved.
libstdc++-v3/ChangeLog:
PR libstdc++/117402
* include/bits/stl_map.h: Add new overloads.
* include/bits/stl_set.h: Same.
* include/bits/unordered_map.h: Same.
* include/bits/unordered_set.h: Same.
* include/bits/hashtable.h: Add supporting overloads.
* include/bits/stl_tree.h: Same.
* include/bits/hashtable_policy.h: Same.
* include/bits/version.def: Add feature macro.
* include/bits/version.h: Regenerate.
* testsuite/23_containers/map/modifiers/hetero/insert.cc: New tests
* testsuite/23_containers/set/modifiers/hetero/insert.cc: Same
* testsuite/23_containers/unordered_map/modifiers/hetero/insert.cc: Same
* testsuite/23_containers/unordered_multimap/modifiers/hetero/insert.cc:
Same.
* testsuite/23_containers/unordered_multiset/modifiers/hetero/insert.cc:
Same.
* testsuite/23_containers/unordered_set/modifiers/hetero/insert.cc:
Same.
---
libstdc++-v3/include/bits/hashtable.h | 85 +++++-----
libstdc++-v3/include/bits/hashtable_policy.h | 61 +++++++-
libstdc++-v3/include/bits/stl_map.h | 126 ++++++++++++++-
libstdc++-v3/include/bits/stl_set.h | 20 +++
libstdc++-v3/include/bits/stl_tree.h | 128 ++++++++++++++--
libstdc++-v3/include/bits/unordered_map.h | 96 ++++++++++++
libstdc++-v3/include/bits/unordered_set.h | 32 ++++
libstdc++-v3/include/bits/version.def | 8 +
libstdc++-v3/include/bits/version.h | 10 ++
.../map/modifiers/hetero/insert.cc | 141 +++++++++++++++++
.../set/modifiers/hetero/insert.cc | 60 ++++++++
.../unordered_map/modifiers/hetero/insert.cc | 145 ++++++++++++++++++
.../modifiers/hetero/insert.cc | 44 ++++++
.../modifiers/hetero/insert.cc | 44 ++++++
.../unordered_set/modifiers/hetero/insert.cc | 75 +++++++++
15 files changed, 1020 insertions(+), 55 deletions(-)
create mode 100644
libstdc++-v3/testsuite/23_containers/map/modifiers/hetero/insert.cc
create mode 100644
libstdc++-v3/testsuite/23_containers/set/modifiers/hetero/insert.cc
create mode 100644
libstdc++-v3/testsuite/23_containers/unordered_map/modifiers/hetero/insert.cc
create mode 100644
libstdc++-v3/testsuite/23_containers/unordered_multimap/modifiers/hetero/insert.cc
create mode 100644
libstdc++-v3/testsuite/23_containers/unordered_multiset/modifiers/hetero/insert.cc
create mode 100644
libstdc++-v3/testsuite/23_containers/unordered_set/modifiers/hetero/insert.cc
diff --git a/libstdc++-v3/include/bits/hashtable.h
b/libstdc++-v3/include/bits/hashtable.h
index 113f5031eaf..e0b2ec630dd 100644
--- a/libstdc++-v3/include/bits/hashtable.h
+++ b/libstdc++-v3/include/bits/hashtable.h
@@ -607,7 +607,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
if constexpr (__unique_keys::value)
{
- if (auto __loc = _M_locate(__k))
+ if (auto __loc = _M_locate_tr(__k))
continue; // Found existing element with equivalent key
else
{
@@ -700,6 +700,13 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
bucket(const key_type& __k) const
{ return _M_bucket_index(this->_M_hash_code(__k)); }
+#ifdef __glibcxx_associative_heterogeneous_insertion // C++26, P2363
+ template <typename _Kt>
+ size_type
+ _M_bucket_tr(const _Kt& __k) const
+ { return _M_bucket_index(this->_M_hash_code_tr(__k)); }
+#endif
+
local_iterator
begin(size_type __bkt)
{
@@ -888,6 +895,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
size_type _M_bucket_index = size_type(-1);
};
+ // This is preserved just for ABI stability.
+ __location_type
+ _M_locate(const key_type& __k) const;
+
// Adaptive lookup to find key, or which bucket it would be in.
// For a container smaller than the small size threshold use a linear
// search through the whole container, just testing for equality.
@@ -902,11 +913,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
// The _M_before pointer might point to _M_before_begin, so must not be
// cast to __node_ptr, and it must not be used to modify *_M_before
// except in non-const member functions, such as erase.
- __location_type
- _M_locate(const key_type& __k) const;
-
- // We would like to extend _M_locate for heterogeneous
- // keys, and use try_emplace as is, but ABI forbids it.
template <typename _HetKey>
__location_type
_M_locate_tr(const _HetKey& __k) const;
@@ -1071,7 +1077,6 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
else
return _M_emplace_multi(__hint, __v);
}
-
__ireturn_type
insert(value_type&& __v)
{
@@ -1095,9 +1100,10 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
std::pair<iterator, bool>
try_emplace(const_iterator, _KType&& __k, _Args&&... __args)
{
+ // Note we ignore the hint argument.
__hash_code __code;
size_type __bkt;
- if (auto __loc = _M_locate(__k))
+ if (auto __loc = _M_locate_tr(__k))
return { iterator(__loc), false };
else
{
@@ -1115,6 +1121,24 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__node._M_node = nullptr;
return { __it, true };
}
+
+#ifdef __glibcxx_associative_heterogeneous_insertion // C++26, P2363
+ template<typename _Kt>
+ std::pair<iterator, bool>
+ _M_insert_tr(_Kt&& __k)
+ {
+ auto __loc = _M_locate_tr(__k);
+ if (__loc)
+ return { iterator(__loc), false };
+
+ _Scoped_node __node(
+ this->_M_allocate_node(std::forward<_Kt>(__k)), this);
+ auto __it = _M_insert_unique_node(
+ __loc._M_bucket_index, __loc._M_hash_code, __node._M_node);
+ __node._M_node = nullptr;
+ return { __it, true };
+ }
+#endif
#endif
void
@@ -1203,7 +1227,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
{
__glibcxx_assert(get_allocator() == __nh.get_allocator());
- if (auto __loc = _M_locate(__nh._M_key()))
+ if (auto __loc = _M_locate_tr(__nh._M_key()))
{
__ret.node = std::move(__nh);
__ret.position = iterator(__loc);
@@ -1331,7 +1355,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
const auto __next = __prev->_M_nxt;
const auto& __node = static_cast<__node_type&>(*__next);
const key_type& __k = _ExtractKey{}(__node._M_v());
- const auto __loc = _M_locate(__k);
+ const auto __loc = _M_locate_tr(__k);
if (__loc)
{
__prev = __next;
@@ -1365,7 +1389,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
--__n_elt;
auto __pos = __i++;
const key_type& __k = _ExtractKey{}(*__pos);
- const auto __loc = _M_locate(__k);
+ const auto __loc = _M_locate_tr(__k);
if (__loc)
continue;
@@ -1948,7 +1972,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
find(const key_type& __k)
-> iterator
- { return iterator(_M_locate(__k)); }
+ { return iterator(_M_locate_tr(__k)); }
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
@@ -1959,7 +1983,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
find(const key_type& __k) const
-> const_iterator
- { return const_iterator(_M_locate(__k)); }
+ { return const_iterator(_M_locate_tr(__k)); }
#ifdef __glibcxx_generic_unordered_lookup // C++ >= 20 && HOSTED
template<typename _Key, typename _Value, typename _Alloc,
@@ -2283,6 +2307,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
return nullptr;
}
+ // This non-inline interface remains just for ABI stability.
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
typename _Hash, typename _RangeHash, typename _Unused,
@@ -2292,32 +2317,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
_M_locate(const key_type& __k) const
-> __location_type
- {
- __location_type __loc;
- const auto __size = size();
-
- if (__size <= __small_size_threshold())
- {
- __loc._M_before = pointer_traits<__node_base_ptr>::
- pointer_to(const_cast<__node_base&>(_M_before_begin));
- while (__loc._M_before->_M_nxt)
- {
- if (this->_M_key_equals(__k, *__loc._M_node()))
- return __loc;
- __loc._M_before = __loc._M_before->_M_nxt;
- }
- __loc._M_before = nullptr; // Didn't find it.
- }
-
- __loc._M_hash_code = this->_M_hash_code(__k);
- __loc._M_bucket_index = _M_bucket_index(__loc._M_hash_code);
-
- if (__size > __small_size_threshold())
- __loc._M_before = _M_find_before_node(__loc._M_bucket_index, __k,
- __loc._M_hash_code);
-
- return __loc;
- }
+ { return _M_locate_tr(__k); }
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
@@ -2328,7 +2328,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
_Hashtable<_Key, _Value, _Alloc, _ExtractKey, _Equal,
_Hash, _RangeHash, _Unused, _RehashPolicy, _Traits>::
_M_locate_tr(const _HetKey& __k) const
- -> __location_type
+ -> __location_type
{
__location_type __loc;
const auto __size = size();
@@ -2418,7 +2418,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__kp = std::__addressof(__key);
}
- if (auto __loc = _M_locate(*__kp))
+ if (auto __loc = _M_locate_tr(*__kp))
// There is already an equivalent node, no insertion.
return { iterator(__loc), false };
else
@@ -2436,6 +2436,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
__node._M_node = nullptr;
return { __pos, true };
}
+
#pragma GCC diagnostic pop
template<typename _Key, typename _Value, typename _Alloc,
@@ -2676,7 +2677,7 @@ _GLIBCXX_BEGIN_NAMESPACE_VERSION
erase(const key_type& __k)
-> size_type
{
- auto __loc = _M_locate(__k);
+ auto __loc = _M_locate_tr(__k);
if (!__loc)
return 0;
diff --git a/libstdc++-v3/include/bits/hashtable_policy.h
b/libstdc++-v3/include/bits/hashtable_policy.h
index 6d7bde1e785..ec082485b18 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -872,6 +872,35 @@ namespace __detail
__throw_out_of_range(__N("unordered_map::at"));
return __ite->second;
}
+
+#if __glibcxx_associative_heterogeneous_insertion
+ // op[] for transparent heterogeneous key
+ template <typename _Kt>
+ mapped_type&
+ _M_index_to_tr(const _Kt& __k);
+
+ // _GLIBCXX_RESOLVE_LIB_DEFECTS
+ // DR 761. unordered_map needs an at() member function.
+ template <typename _Kt>
+ mapped_type&
+ _M_at_tr(const _Kt& __k)
+ {
+ auto __ite = static_cast<__hashtable*>(this)->_M_find_tr(__k);
+ if (!__ite._M_cur)
+ __throw_out_of_range(__N("unordered_map::at"));
+ return __ite->second;
+ }
+
+ template <typename _Kt>
+ const mapped_type&
+ _M_at_tr(const _Kt& __k) const
+ {
+ auto __ite = static_cast<const __hashtable*>(this)->_M_find_tr(__k);
+ if (!__ite._M_cur)
+ __throw_out_of_range(__N("unordered_map::at"));
+ return __ite->second;
+ }
+#endif
};
template<typename _Key, typename _Val, typename _Alloc, typename _Equal,
@@ -901,6 +930,35 @@ namespace __detail
return __pos->second;
}
+#if __glibcxx_associative_heterogeneous_insertion
+ // op[] for heterogeneous keys
+ template<typename _Key, typename _Val, typename _Alloc, typename _Equal,
+ typename _Hash, typename _RangeHash, typename _Unused,
+ typename _RehashPolicy, typename _Traits>
+ template <typename _Kt>
+ auto
+ _Map_base<_Key, pair<const _Key, _Val>, _Alloc, _Select1st, _Equal,
+ _Hash, _RangeHash, _Unused, _RehashPolicy, _Traits, true>::
+ _M_index_to_tr(const _Kt& __k)
+ -> mapped_type&
+ {
+ __hashtable* __h = static_cast<__hashtable*>(this);
+ __hash_code __code = __h->_M_hash_code_tr(__k);
+ size_t __bkt = __h->_M_bucket_index(__code);
+ if (auto __node = __h->_M_find_node_tr(__bkt, __k, __code))
+ return __node->_M_v().second;
+
+ typename __hashtable::_Scoped_node __node {
+ __h, std::piecewise_construct,
+ std::tuple<key_type>(__k), std::tuple<>()
+ };
+ auto __pos
+ = __h->_M_insert_unique_node(__bkt, __code, __node._M_node);
+ __node._M_node = nullptr;
+ return __pos->second;
+ }
+#endif
+
template<typename _Key, typename _Val, typename _Alloc, typename _Equal,
typename _Hash, typename _RangeHash, typename _Unused,
typename _RehashPolicy, typename _Traits>
@@ -1413,8 +1471,7 @@ namespace __detail
template<typename _Kt>
bool
_M_key_equals_tr(const _Kt& __k,
- const _Hash_node_value<_Value,
- __hash_cached::value>& __n) const
+ const _Hash_node_value<_Value, __hash_cached::value>& __n) const
{
static_assert(
__is_invocable<const _Equal&, const _Kt&, const _Key&>{},
diff --git a/libstdc++-v3/include/bits/stl_map.h
b/libstdc++-v3/include/bits/stl_map.h
index 9a0f3086188..fa18ad9acee 100644
--- a/libstdc++-v3/include/bits/stl_map.h
+++ b/libstdc++-v3/include/bits/stl_map.h
@@ -511,6 +511,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
max_size() const _GLIBCXX_NOEXCEPT
{ return _M_t.max_size(); }
+ ///@{
// [23.3.1.2] element access
/**
* @brief Subscript ( @c [] ) access to %map data.
@@ -560,6 +561,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
#endif
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_tree_key<map> _Kt>
+ mapped_type&
+ operator[](_Kt&& __k)
+ { return try_emplace(std::forward<_Kt>(__k)).first->second; }
+#endif
+ ///@}
+
+ ///@{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 464. Suggestion for new member functions in standard containers.
/**
@@ -578,6 +588,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
return (*__i).second;
}
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_tree_key<map> _Kt>
+ mapped_type&
+ at(const _Kt& __k)
+ {
+ iterator __i = lower_bound(__k);
+ if (__i == end() || key_comp()(__k, (*__i).first))
+ __throw_out_of_range(__N("map::at"));
+ return (*__i).second;
+ }
+#endif
+
const mapped_type&
at(const key_type& __k) const
{
@@ -587,6 +609,19 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
return (*__i).second;
}
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_tree_key<map> _Kt>
+ const mapped_type&
+ at(const _Kt& __k) const
+ {
+ const_iterator __i = lower_bound(__k);
+ if (__i == end() || key_comp()(__k, (*__i).first))
+ __throw_out_of_range(__N("map::at"));
+ return (*__i).second;
+ }
+#endif
+ ///@}
+
// modifiers
#if __cplusplus >= 201103L
/**
@@ -728,6 +763,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
#endif // C++17
#ifdef __glibcxx_map_try_emplace // C++ >= 17 && HOSTED
+ ///@{
/**
* @brief Attempts to build and insert a std::pair into the %map.
*
@@ -781,6 +817,25 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
return {__i, false};
}
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_tree_key<map> _Kt, typename ..._Args>
+ pair<iterator, bool>
+ try_emplace(_Kt&& __k, _Args&&... __args)
+ {
+ iterator __i = lower_bound(__k);
+ if (__i == end() || key_comp()(__k, (*__i).first))
+ {
+ __i = _M_t._M_emplace_hint_unique(__i, std::piecewise_construct,
+ std::forward_as_tuple(std::forward<_Kt>(__k)),
+ std::forward_as_tuple(std::forward<_Args>(__args)...));
+ return {__i, true};
+ }
+ return {__i, false};
+ }
+#endif
+ ///@}
+
+ ///@{
/**
* @brief Attempts to build and insert a std::pair into the %map.
*
@@ -845,6 +900,26 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
#endif
+#ifdef __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_tree_key<map> _Kt, typename ..._Args>
+ iterator
+ try_emplace(const_iterator __hint, _Kt&& __k, _Args&&... __args)
+ {
+ iterator __i;
+ auto __true_hint = _M_t._M_get_insert_hint_unique_pos_tr(__hint, __k);
+ if (__true_hint.second)
+ __i = _M_t._M_emplace_hint_unique(iterator(__true_hint.second),
+ std::piecewise_construct,
+ std::forward_as_tuple(std::forward<_Kt>(__k)),
+ std::forward_as_tuple(std::forward<_Args>(__args)...));
+ else
+ __i = iterator(__true_hint.first);
+ return __i;
+ }
+#endif
+ ///@}
+
+ ///@{
/**
* @brief Attempts to insert a std::pair into the %map.
* @param __x Pair to be inserted (see std::make_pair for easy
@@ -896,7 +971,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
return _M_t._M_emplace_unique(std::forward<_Pair>(__x));
}
#endif
- /// @}
+ ///@}
#if __cplusplus >= 201103L
/**
@@ -929,6 +1004,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
#endif
+ ///@{
/**
* @brief Attempts to insert a std::pair into the %map.
* @param __position An iterator that serves as a hint as to where the
@@ -992,6 +1068,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
{ _M_t._M_insert_range_unique(__first, __last); }
#if __cplusplus > 201402L
+ ///@{
/**
* @brief Attempts to insert or assign a std::pair into the %map.
* @param __k Key to use for finding a possibly existing pair in
@@ -1046,6 +1123,29 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
return {__i, false};
}
+#ifdef __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_tree_key<map> _Kt, typename _Mapped>
+ pair<iterator, bool>
+ insert_or_assign(_Kt&& __k, _Mapped&& __obj)
+ {
+ iterator __i = lower_bound(__k);
+ if (__i == end() || key_comp()(__k, (*__i).first))
+ {
+ __i = _M_t._M_emplace_hint_unique(__i,
+ std::piecewise_construct,
+ std::forward_as_tuple(std::forward<_Kt>(__k)),
+ std::forward_as_tuple(std::forward<_Mapped>(__obj)));
+ return {__i, true};
+ }
+ (*__i).second = std::forward<_Mapped>(__obj);
+ return {__i, false};
+ }
+#endif
+ ///@}
+#endif
+
+#if __cplusplus > 201402L
+ ///@{
/**
* @brief Attempts to insert or assign a std::pair into the %map.
* @param __hint An iterator that serves as a hint as to where the
@@ -1105,9 +1205,33 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
(*__i).second = std::forward<_Obj>(__obj);
return __i;
}
+
+#ifdef __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_tree_key<map> _Kt, typename _Mapped>
+ iterator
+ insert_or_assign(const_iterator __hint, _Kt&& __k, _Mapped&& __obj)
+ {
+ iterator __i;
+ auto __true_hint =
+ _M_t._M_get_insert_hint_unique_pos_tr(__hint, __k);
+ if (__true_hint.second)
+ {
+ return _M_t._M_emplace_hint_unique(
+ iterator(__true_hint.second),
+ std::piecewise_construct,
+ std::forward_as_tuple(std::forward<_Kt>(__k)),
+ std::forward_as_tuple(std::forward<_Mapped>(__obj)));
+ }
+ __i = iterator(__true_hint.first);
+ (*__i).second = std::forward<_Mapped>(__obj);
+ return __i;
+ }
+#endif
+ ///@}
#endif
#if __cplusplus >= 201103L
+ ///@{
// _GLIBCXX_RESOLVE_LIB_DEFECTS
// DR 130. Associative erase should return an iterator.
/**
diff --git a/libstdc++-v3/include/bits/stl_set.h
b/libstdc++-v3/include/bits/stl_set.h
index 47f7b7062b8..26656d387c4 100644
--- a/libstdc++-v3/include/bits/stl_set.h
+++ b/libstdc++-v3/include/bits/stl_set.h
@@ -517,6 +517,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
#endif
+ ///@{
/**
* @brief Attempts to insert an element into the %set.
* @param __x Element to be inserted.
@@ -548,6 +549,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
#endif
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_tree_key<set> _Kt>
+ pair<iterator, bool>
+ insert(_Kt&& __x)
+ {
+ auto __p = _M_t._M_insert_unique(__x);
+ return {__p.first, __p.second};
+ }
+#endif
+ ///@}
+
+ ///@{
/**
* @brief Attempts to insert an element into the %set.
* @param __position An iterator that serves as a hint as to where the
@@ -576,6 +589,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
insert(const_iterator __position, value_type&& __x)
{ return _M_t._M_insert_unique_(__position, std::move(__x)); }
#endif
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_tree_key<set> _Kt>
+ iterator
+ insert(const_iterator __position, _Kt&& __x)
+ { return _M_t._M_insert_unique_(__position, __x); }
+#endif
+ ///@}
/**
* @brief A template function that attempts to insert a range
diff --git a/libstdc++-v3/include/bits/stl_tree.h
b/libstdc++-v3/include/bits/stl_tree.h
index 44ffa626183..6147c340778 100644
--- a/libstdc++-v3/include/bits/stl_tree.h
+++ b/libstdc++-v3/include/bits/stl_tree.h
@@ -1459,6 +1459,12 @@ namespace __rb_tree
pair<_Base_ptr, _Base_ptr>
_M_get_insert_unique_pos(const key_type& __k);
+#ifdef __glibcxx_associative_heterogeneous_insertion // C++26, P2363
+ template <typename _Kt>
+ pair<_Base_ptr, _Base_ptr>
+ _M_get_insert_unique_pos_tr(const _Kt& __k);
+#endif
+
pair<_Base_ptr, _Base_ptr>
_M_get_insert_equal_pos(const key_type& __k);
@@ -1466,6 +1472,13 @@ namespace __rb_tree
_M_get_insert_hint_unique_pos(const_iterator __pos,
const key_type& __k);
+#ifdef __glibcxx_associative_heterogeneous_insertion // C++26, P2363
+ template <typename _Kt>
+ pair<_Base_ptr, _Base_ptr>
+ _M_get_insert_hint_unique_pos_tr(
+ const_iterator __pos, const _Kt& __k);
+#endif
+
pair<_Base_ptr, _Base_ptr>
_M_get_insert_hint_equal_pos(const_iterator __pos,
const key_type& __k);
@@ -1539,7 +1552,7 @@ namespace __rb_tree
_M_lower_bound(_Base_ptr __x, _Base_ptr __y,
const _Key& __k) const;
-#if __glibcxx_associative_heterogeneous_erasure // C++23, P2077
+#ifdef __glibcxx_associative_heterogeneous_erasure // C++23, P2077
template <typename _HetKey>
_Base_ptr
_M_lower_bound_tr(
@@ -1550,7 +1563,7 @@ namespace __rb_tree
_M_upper_bound(_Base_ptr __x, _Base_ptr __y,
const _Key& __k) const;
-#if __glibcxx_associative_heterogeneous_erasure // C++23, P2077
+#ifdef __glibcxx_associative_heterogeneous_erasure // C++23, P2077
template <typename _HetKey>
_Base_ptr
_M_upper_bound_tr(
@@ -1864,7 +1877,7 @@ namespace __rb_tree
size_type
erase(const key_type& __x);
-#if __glibcxx_associative_heterogeneous_erasure // C++23, P2077
+#ifdef __glibcxx_associative_heterogeneous_erasure // C++23, P2077
template <typename _HetKey>
size_type
_M_erase_tr(const _HetKey& __x);
@@ -2068,7 +2081,7 @@ namespace __rb_tree
_M_move_assign(_Rb_tree&, false_type);
#endif
-#if __glibcxx_node_extract // >= C++17
+#ifdef __glibcxx_node_extract // >= C++17
static _Node_ptr
_S_adapt(typename _Node_alloc_traits::pointer __ptr)
{
@@ -2218,7 +2231,7 @@ namespace __rb_tree
return __nh;
}
-#if __glibcxx_associative_heterogeneous_erasure // C++23, P2077
+#ifdef __glibcxx_associative_heterogeneous_erasure // C++23, P2077
template <typename _HetKey>
node_type
_M_extract_tr(const _HetKey& __k)
@@ -2319,6 +2332,10 @@ namespace __rb_tree
_M_node(__t._M_create_node(std::forward<_Args>(__args)...))
{ }
+ explicit _Auto_node(_Rb_tree& __t)
+ : _M_t(__t), _M_node(nullptr)
+ { }
+
~_Auto_node()
{
if (_M_node)
@@ -2645,7 +2662,7 @@ namespace __rb_tree
return __y;
}
-#if __glibcxx_associative_heterogeneous_erasure // C++23, P2077
+#ifdef __glibcxx_associative_heterogeneous_erasure // C++23, P2077
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
template <typename _HetKey>
@@ -2679,7 +2696,7 @@ namespace __rb_tree
return __y;
}
-#if __glibcxx_associative_heterogeneous_erasure // C++23, P2077
+#ifdef __glibcxx_associative_heterogeneous_erasure // C++23, P2077
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
template <typename _HetKey>
@@ -2826,6 +2843,39 @@ namespace __rb_tree
return _Res(__j._M_node, _Base_ptr());
}
+#ifdef __glibcxx_associative_heterogeneous_insertion // C++26, P2363
+ template<typename _Key, typename _Val, typename _KeyOfValue,
+ typename _Compare, typename _Alloc>
+ template <typename _Kt>
+ auto
+ _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
+ _M_get_insert_unique_pos_tr(const _Kt& __k)
+ -> pair<_Base_ptr, _Base_ptr>
+ {
+ typedef pair<_Base_ptr, _Base_ptr> _Res;
+ _Base_ptr __x = _M_begin();
+ _Base_ptr __y = _M_end();
+ bool __comp = true;
+ while (__x)
+ {
+ __y = __x;
+ __comp = _M_key_compare(__k, _S_key(__x));
+ __x = __comp ? _S_left(__x) : _S_right(__x);
+ }
+ iterator __j = iterator(__y);
+ if (__comp)
+ {
+ if (__j == begin())
+ return _Res(__x, __y);
+ else
+ --__j;
+ }
+ if (_M_key_compare(_S_key(__j._M_node), __k))
+ return _Res(__x, __y);
+ return _Res(__j._M_node, _Base_ptr());
+ }
+#endif
+
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
pair<typename _Rb_tree<_Key, _Val, _KeyOfValue,
@@ -2952,6 +3002,64 @@ namespace __rb_tree
return _Res(__position._M_node, _Base_ptr());
}
+#ifdef __glibcxx_associative_heterogeneous_insertion // C++26, P2363
+ template<typename _Key, typename _Val, typename _KeyOfValue,
+ typename _Compare, typename _Alloc>
+ template <typename _Kt>
+ auto
+ _Rb_tree<_Key, _Val, _KeyOfValue, _Compare, _Alloc>::
+ _M_get_insert_hint_unique_pos_tr(
+ const_iterator __position, const _Kt& __k)
+ -> pair<_Base_ptr, _Base_ptr>
+ {
+ typedef pair<_Base_ptr, _Base_ptr> _Res;
+
+ // end()
+ if (__position._M_node == _M_end())
+ {
+ if (size() > 0 && _M_key_compare(_S_key(_M_rightmost()), __k))
+ return _Res(_Base_ptr(), _M_rightmost());
+ else
+ return _M_get_insert_unique_pos_tr(__k);
+ }
+ else if (_M_key_compare(__k, _S_key(__position._M_node)))
+ {
+ // First, try before...
+ iterator __before(__position._M_node);
+ if (__position._M_node == _M_leftmost()) // begin()
+ return _Res(_M_leftmost(), _M_leftmost());
+ else if (_M_key_compare(_S_key((--__before)._M_node), __k))
+ {
+ if (!_S_right(__before._M_node))
+ return _Res(_Base_ptr(), __before._M_node);
+ else
+ return _Res(__position._M_node, __position._M_node);
+ }
+ else
+ return _M_get_insert_unique_pos_tr(__k);
+ }
+ else if (_M_key_compare(_S_key(__position._M_node), __k))
+ {
+ // ... then try after.
+ iterator __after(__position._M_node);
+ if (__position._M_node == _M_rightmost())
+ return _Res(_Base_ptr(), _M_rightmost());
+ else if (_M_key_compare(__k, _S_key((++__after)._M_node)))
+ {
+ if (!_S_right(__position._M_node))
+ return _Res(_Base_ptr(), __position._M_node);
+ else
+ return _Res(__after._M_node, __after._M_node);
+ }
+ else
+ return _M_get_insert_unique_pos_tr(__k);
+ }
+ else
+ // Equivalent keys.
+ return _Res(__position._M_node, _Base_ptr());
+ }
+#endif
+
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
#if __cplusplus >= 201103L
@@ -3171,7 +3279,7 @@ namespace __rb_tree
return __z._M_insert(__res);
return __z._M_insert_equal_lower();
}
-#endif
+#endif // >= C++11
template<typename _Key, typename _Val, typename _KeyOfValue,
@@ -3211,7 +3319,7 @@ namespace __rb_tree
return __old_size - size();
}
-#if __glibcxx_associative_heterogeneous_erasure // C++23, P2077
+#ifdef __glibcxx_associative_heterogeneous_erasure // C++23, P2077
template<typename _Key, typename _Val, typename _KeyOfValue,
typename _Compare, typename _Alloc>
template <typename _HetKey>
@@ -3334,7 +3442,7 @@ namespace __rb_tree
};
#endif // C++17
-#if __glibcxx_associative_heterogeneous_erasure // C++23, P2077
+#ifdef __glibcxx_associative_heterogeneous_erasure // C++23, P2077
template <typename _Kt, typename _Container>
concept __heterogeneous_tree_key =
__transparent_comparator<typename _Container::key_compare> &&
diff --git a/libstdc++-v3/include/bits/unordered_map.h
b/libstdc++-v3/include/bits/unordered_map.h
index 193c1795c07..8313fa83dba 100644
--- a/libstdc++-v3/include/bits/unordered_map.h
+++ b/libstdc++-v3/include/bits/unordered_map.h
@@ -456,6 +456,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
emplace(_Args&&... __args)
{ return _M_h.emplace(std::forward<_Args>(__args)...); }
+ ///@{
/**
* @brief Attempts to build and insert a std::pair into the
* %unordered_map.
@@ -520,6 +521,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
#endif // node_extract
#ifdef __glibcxx_unordered_map_try_emplace // C++ >= 17 && HOSTED
+ ///@{
/**
* @brief Attempts to build and insert a std::pair into the
* %unordered_map.
@@ -558,6 +560,18 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
std::forward<_Args>(__args)...);
}
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_hash_key<unordered_map> _Kt, typename ..._Args>
+ pair<iterator, bool>
+ try_emplace(_Kt&& __k, _Args&&... __args)
+ {
+ return _M_h.try_emplace(cend(),
+ std::forward<_Kt>(__k), std::forward<_Args>(__args)...);
+ }
+#endif
+ ///@}
+
+ ///@{
/**
* @brief Attempts to build and insert a std::pair into the
* %unordered_map.
@@ -605,6 +619,17 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
}
#endif // __glibcxx_unordered_map_try_emplace
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_hash_key<unordered_map> _Kt, typename ..._Args>
+ iterator
+ try_emplace(const_iterator __hint, _Kt&& __k, _Args&&... __args)
+ {
+ return _M_h.try_emplace(__hint,
+ std::forward<_Kt>(__k), std::forward<_Args>(__args)...).first;
+ }
+#endif
+ ///@}
+
///@{
/**
* @brief Attempts to insert a std::pair into the %unordered_map.
@@ -722,6 +747,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
#endif
#ifdef __glibcxx_unordered_map_try_emplace // >= C++17 && HOSTED
+ ///@{
/**
* @brief Attempts to insert a std::pair into the %unordered_map.
* @param __k Key to use for finding a possibly existing pair in
@@ -765,6 +791,21 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
return __ret;
}
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_hash_key<unordered_map> _Kt, typename _Mapped>
+ pair<iterator, bool>
+ insert_or_assign(_Kt&& __k, _Mapped&& __obj)
+ {
+ auto __ret = _M_h.try_emplace(
+ cend(), std::forward<_Kt>(__k), std::forward<_Mapped>(__obj));
+ if (!__ret.second)
+ __ret.first->second = std::forward<_Mapped>(__obj);
+ return __ret;
+ }
+#endif
+ ///@}
+
+ ///@{
/**
* @brief Attempts to insert a std::pair into the %unordered_map.
* @param __hint An iterator that serves as a hint as to where the
@@ -813,6 +854,20 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
__ret.first->second = std::forward<_Obj>(__obj);
return __ret.first;
}
+
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_hash_key<unordered_map> _Kt, typename _Mapped>
+ iterator
+ insert_or_assign(const_iterator __hint, _Kt&& __k, _Mapped&& __obj)
+ {
+ auto __ret = _M_h.try_emplace(__hint,
+ std::forward<_Kt>(__k), std::forward<_Mapped>(__obj));
+ if (!__ret.second)
+ __ret.first->second = std::forward<_Mapped>(__obj);
+ return __ret.first;
+ }
+#endif
+ ///@}
#endif // unordered_map_try_emplace
///@{
@@ -1089,6 +1144,15 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
mapped_type&
operator[](key_type&& __k)
{ return _M_h[std::move(__k)]; }
+
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_hash_key<unordered_map> _Kt>
+ mapped_type&
+ operator[](_Kt&& __k)
+ {
+ return try_emplace(std::forward<_Kt>(__k)).first->second;
+ }
+#endif
///@}
///@{
@@ -1103,9 +1167,23 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
at(const key_type& __k)
{ return _M_h.at(__k); }
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_hash_key<unordered_map> _Kt>
+ mapped_type&
+ at(const _Kt& __k)
+ { return _M_h._M_at_tr(__k); }
+#endif
+
const mapped_type&
at(const key_type& __k) const
{ return _M_h.at(__k); }
+
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_hash_key<unordered_map> _Kt>
+ const mapped_type&
+ at(const _Kt& __k) const
+ { return _M_h._M_at_tr(__k); }
+#endif
///@}
// bucket interface.
@@ -1129,6 +1207,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
bucket_size(size_type __n) const
{ return _M_h.bucket_size(__n); }
+ ///@{
/*
* @brief Returns the bucket index of a given element.
* @param __key A key instance.
@@ -1138,6 +1217,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
bucket(const key_type& __key) const
{ return _M_h.bucket(__key); }
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_hash_key<unordered_map> _Kt>
+ size_type
+ bucket(const _Kt& __key) const
+ { return _M_h._M_bucket_tr(__key); }
+#endif
+ ///@}
+
/**
* @brief Returns a read/write iterator pointing to the first bucket
* element.
@@ -2176,6 +2263,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
bucket_size(size_type __n) const
{ return _M_h.bucket_size(__n); }
+ ///@{
/*
* @brief Returns the bucket index of a given element.
* @param __key A key instance.
@@ -2185,6 +2273,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
bucket(const key_type& __key) const
{ return _M_h.bucket(__key); }
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_hash_key<unordered_multimap> _Kt>
+ size_type
+ bucket(const _Kt& __key) const
+ { return _M_h._M_bucket_tr(__key); }
+#endif
+ ///@}
+
/**
* @brief Returns a read/write iterator pointing to the first bucket
* element.
diff --git a/libstdc++-v3/include/bits/unordered_set.h
b/libstdc++-v3/include/bits/unordered_set.h
index b15f25be7fb..35dfc1d42b0 100644
--- a/libstdc++-v3/include/bits/unordered_set.h
+++ b/libstdc++-v3/include/bits/unordered_set.h
@@ -493,6 +493,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
std::pair<iterator, bool>
insert(value_type&& __x)
{ return _M_h.insert(std::move(__x)); }
+
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_hash_key<unordered_set> _Kt>
+ std::pair<iterator, bool>
+ insert(_Kt&& __x)
+ { return _M_h._M_insert_tr(std::forward<_Kt>(__x)); }
+#endif
///@}
///@{
@@ -522,6 +529,13 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
iterator
insert(const_iterator __hint, value_type&& __x)
{ return _M_h.insert(__hint, std::move(__x)); }
+
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_hash_key<unordered_set> _Kt>
+ iterator
+ insert(const_iterator, _Kt&& __x)
+ { return _M_h._M_insert_tr(std::forward<_Kt>(__x)).first; }
+#endif
///@}
/**
@@ -876,6 +890,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
bucket_size(size_type __n) const
{ return _M_h.bucket_size(__n); }
+ ///@{
/*
* @brief Returns the bucket index of a given element.
* @param __key A key instance.
@@ -885,6 +900,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
bucket(const key_type& __key) const
{ return _M_h.bucket(__key); }
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_hash_key<unordered_set> _Kt>
+ size_type
+ bucket(const _Kt& __key) const
+ { return _M_h._M_bucket_tr(__key); }
+#endif
+ ///@}
+
///@{
/**
* @brief Returns a read-only (constant) iterator pointing to the first
@@ -1884,6 +1907,7 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
bucket_size(size_type __n) const
{ return _M_h.bucket_size(__n); }
+ ///@{
/*
* @brief Returns the bucket index of a given element.
* @param __key A key instance.
@@ -1893,6 +1917,14 @@ _GLIBCXX_BEGIN_NAMESPACE_CONTAINER
bucket(const key_type& __key) const
{ return _M_h.bucket(__key); }
+#if __glibcxx_associative_heterogeneous_insertion // P2363R5
+ template <__heterogeneous_hash_key<unordered_multiset> _Kt>
+ size_type
+ bucket(const _Kt& __key) const
+ { return _M_h._M_bucket_tr(__key); }
+#endif
+ ///@}
+
///@{
/**
* @brief Returns a read-only (constant) iterator pointing to the first
diff --git a/libstdc++-v3/include/bits/version.def
b/libstdc++-v3/include/bits/version.def
index 937ea09ad78..a963c21ee5c 100644
--- a/libstdc++-v3/include/bits/version.def
+++ b/libstdc++-v3/include/bits/version.def
@@ -2262,6 +2262,14 @@ ftms = {
};
};
+ftms = {
+ name = associative_heterogeneous_insertion;
+ values = {
+ v = 202306;
+ cxxmin = 26;
+ };
+};
+
// Standard test specifications.
stds[97] = ">= 199711L";
stds[03] = ">= 199711L";
diff --git a/libstdc++-v3/include/bits/version.h
b/libstdc++-v3/include/bits/version.h
index ed1794f4c0d..13bf1c5eaac 100644
--- a/libstdc++-v3/include/bits/version.h
+++ b/libstdc++-v3/include/bits/version.h
@@ -2536,4 +2536,14 @@
#endif /* !defined(__cpp_lib_is_implicit_lifetime) */
#undef __glibcxx_want_is_implicit_lifetime
+#if !defined(__cpp_lib_associative_heterogeneous_insertion)
+# if (__cplusplus > 202302L)
+# define __glibcxx_associative_heterogeneous_insertion 202306L
+# if defined(__glibcxx_want_all) ||
defined(__glibcxx_want_associative_heterogeneous_insertion)
+# define __cpp_lib_associative_heterogeneous_insertion 202306L
+# endif
+# endif
+#endif /* !defined(__cpp_lib_associative_heterogeneous_insertion) */
+#undef __glibcxx_want_associative_heterogeneous_insertion
+
#undef __glibcxx_want_all
diff --git
a/libstdc++-v3/testsuite/23_containers/map/modifiers/hetero/insert.cc
b/libstdc++-v3/testsuite/23_containers/map/modifiers/hetero/insert.cc
new file mode 100644
index 00000000000..e402cc28a5c
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/map/modifiers/hetero/insert.cc
@@ -0,0 +1,141 @@
+// { dg-do compile { target c++26 } }
+
+#include <map>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <compare>
+#include <cstring>
+#include <testsuite_hooks.h>
+
+struct X {
+ std::string s;
+ friend auto operator<=>(X const& a, X const& b) = default;
+};
+
+struct Y {
+ std::string_view s;
+ Y(std::string_view sv) : s(sv) {}
+ Y(std::string const& sv) : s(sv) {}
+ operator X() const { return X(std::basic_string(s)); }
+ friend auto operator<=>(Y const& a, Y const& b) = default;
+ friend auto operator<=>(X const& a, Y const& b) { return a.s <=> b.s; }
+};
+
+struct Sum {
+ int i{};
+ Sum() = default;
+ Sum(const int a, const int b = 0) : i(a + b) {}
+ operator int() { return i; }
+ friend bool operator<=>(Sum const& a, Sum const& b) = default;
+};
+
+using cmp = std::less<void>;
+
+void test1() // op[]
+{
+ std::map<X, Sum, cmp> amap{cmp{}};
+ amap.insert({{X{"abc"}, 1}, {X{"def"}, 2}, {X{"ghi"}, 3}});
+
+ amap[(Y{std::string_view{"deg"}})] = Sum{4};
+ VERIFY(amap.size() == 4);
+
+ // no const overload.
+ // auto const& amapr{amap};
+ // static_assert(!requires{ amapr[(Y{std::string_view{"deg"}})]; });
+}
+
+void test2() // at()
+{
+ std::map<X, Sum, cmp> amap{cmp{}};
+ amap.insert({{X{"abc"}, 1}, {X{"def"}, 2}, {X{"ghi"}, 3}});
+
+ VERIFY(Sum{3} == amap.at(Y{std::string_view{"def"}}));
+ VERIFY(amap.size() == 3);
+
+ amap.at(Y{std::string_view{"deg"}}) = 4;
+ VERIFY(amap.size() == 4);
+
+ auto const& amapr{amap};
+ VERIFY(Sum{4} == amapr.at(Y{std::string_view{"deg"}}));
+ try
+ {
+ amapr.at(Y{std::string_view{"deh"}});
+ VERIFY(false); // Should have thrown.
+ }
+ catch (std::out_of_range&) { VERIFY(amapr.size() == 4); }
+ catch (...) { VERIFY(false); } // Wrong exception.
+}
+
+void test3() // try_emplace
+{
+ std::map<X, Sum, cmp> amap{cmp{}};
+ amap.insert({{X{"abc"}, 1}, {X{"def"}, 2}, {X{"ghi"}, 3}});
+
+ { // Fail, already there
+ auto [it, res] = amap.try_emplace(Y{std::string{"def"}}, 4, 5);
+ VERIFY(!res);
+ VERIFY(amap.size() == 3);
+ }
+ { // Succeed
+ auto [it, res] = amap.try_emplace(Y{std::string{"deg"}}, 4, 5);
+ VERIFY(res);
+ VERIFY(it->first == X{"deg"});
+ VERIFY(it->second == Sum{9});
+ VERIFY(amap.size() == 4);
+ }
+ { // Hinted, fail
+ auto it = amap.try_emplace(amap.begin(), Y{std::string{"def"}}, 4, 5);
+ VERIFY(amap.size() == 3);
+ VERIFY(it->first == X{"def"});
+ VERIFY(it->second == Sum{3});
+ }
+ { // Hinted, succeed
+ auto it = amap.try_emplace(amap.begin(), Y{std::string{"deg"}}, 4, 5);
+ VERIFY(amap.size() == 4);
+ VERIFY(it->first == X{"deg"});
+ VERIFY(it->second == Sum{9});
+ }
+}
+
+void test4() // insert_or_assign
+{
+ std::map<X, Sum, cmp> amap{cmp{}};
+ amap.insert({{X{"abc"}, 1}, {X{"def"}, 2}, {X{"ghi"}, 3}});
+
+ { // Fail
+ auto [it, res] = amap.insert_or_assign(Y{std::string{"def"}}, 4);
+ VERIFY(amap.size() == 3);
+ VERIFY(!res);
+ VERIFY(it->first == X{"def"});
+ VERIFY(it->second == Sum{4});
+ }
+ { // Succeed
+ auto [it, res] = amap.insert_or_assign(Y{std::string{"deg"}}, 5);
+ VERIFY(amap.size() == 4);
+ VERIFY(res);
+ VERIFY(it->first == X{"deg"});
+ VERIFY(it->second == Sum{5});
+ }
+
+ { // Hinted, fail
+ auto it = amap.insert_or_assign(amap.begin(), Y{std::string{"def"}}, 6);
+ VERIFY(amap.size() == 4);
+ VERIFY(it->first == X{"def"});
+ VERIFY(it->second == Sum{4});
+ }
+ { // Hinted, succeed
+ auto it = amap.insert_or_assign(amap.begin(), Y{std::string{"deh"}}, 7);
+ VERIFY(amap.size() == 5);
+ VERIFY(it->first == X{"deh"});
+ VERIFY(it->second == Sum{7});
+ }
+}
+
+int main()
+{
+ test1();
+ test2();
+ test3();
+ test4();
+}
diff --git
a/libstdc++-v3/testsuite/23_containers/set/modifiers/hetero/insert.cc
b/libstdc++-v3/testsuite/23_containers/set/modifiers/hetero/insert.cc
new file mode 100644
index 00000000000..765ea405a27
--- /dev/null
+++ b/libstdc++-v3/testsuite/23_containers/set/modifiers/hetero/insert.cc
@@ -0,0 +1,60 @@
+// { dg-do compile { target c++26 } }
+
+#include <set>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <compare>
+#include <cstring>
+#include <testsuite_hooks.h>
+
+struct X {
+ std::string s;
+ friend auto operator<=>(X const& a, X const& b) = default;
+};
+
+struct Y {
+ std::string_view s;
+ Y(std::string_view sv) : s(sv) {}
+ Y(std::string const& sv) : s(sv) {}
+ operator X() const { return X(std::basic_string(s)); }
+ friend auto operator<=>(Y const& a, Y const& b) = default;
+ friend auto operator<=>(X const& a, Y const& b) { return a.s <=> b.s; }
+};
+
+using cmp = std::less<void>;
+
+void test1() // insert
+{
+ std::set<X, cmp> aset{cmp{}};
+ aset.insert({X{"abc"}, X{"def"}, X{"ghi"}});
+
+ { // Fail
+ auto [it, res] = aset.insert(Y{std::string{"def"}});
+ VERIFY(!res);
+ VERIFY(aset.size() == 3);
+ VERIFY(it->s == std::string{"def"});
+ }
+ { // Succeed
+ auto [it, res] = aset.insert(Y{std::string{"deg"}});
+ VERIFY(res);
+ VERIFY(aset.size() == 4);
+ VERIFY(it->s == std::string{"deg"});
+ }
+
+ { // Hinted, fail
+ auto it = aset.insert(aset.begin(), Y{std::string{"def"}});
+ VERIFY(aset.size() == 4);
+ VERIFY(it->s == std::string{"def"});
+ }
+ { // Hinted, succeed
+ auto it = aset.insert(aset.begin(), Y{std::string{"deh"}});
+ VERIFY(aset.size() == 5);
+ VERIFY(it->s == std::string{"deh"});
+ }
+}
+
+int main()
+{
+ test1();
+}
diff --git
a/libstdc++-v3/testsuite/23_containers/unordered_map/modifiers/hetero/insert.cc
b/libstdc++-v3/testsuite/23_containers/unordered_map/modifiers/hetero/insert.cc
new file mode 100644
index 00000000000..a6d9181123a
--- /dev/null
+++
b/libstdc++-v3/testsuite/23_containers/unordered_map/modifiers/hetero/insert.cc
@@ -0,0 +1,145 @@
+// { dg-do compile { target c++26 } }
+
+#include <unordered_map>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <functional>
+#include <compare>
+#include <testsuite_hooks.h>
+
+struct X {
+ std::string s;
+ friend bool operator==(X const& a, X const& b) = default;
+};
+
+struct Y {
+ std::string_view s;
+ Y(std::string_view sv) : s(sv) {}
+ Y(std::string const& sv) : s(sv) {}
+ operator X() const { return X(std::basic_string(s)); }
+ friend bool operator==(Y const& a, Y const& b) = default;
+ friend bool operator==(X const& a, Y const& b) { return a.s == b.s; }
+};
+
+struct Hash {
+ using is_transparent = void;
+ template <typename T>
+ auto operator()(T const& t) const { return
std::hash<decltype(T::s)>{}(t.s); }
+};
+
+using Equal = std::equal_to<void>;
+
+struct Sum {
+ int i = 0;
+ Sum(int a = 0, int b = 0) : i(a + b) {}
+ operator int() const { return i; }
+};
+
+void test1() // op[]
+{
+ std::unordered_map<X, Sum, Hash, Equal> amap;
+ amap.insert({{X{"abc"}, 1}, {X{"def"}, 2}, {X{"ghi"}, 3}});
+
+ amap[ Y{std::string_view{"deg"}} ] = Sum(4);
+ VERIFY(amap.size() == 4);
+}
+
+void test2() // at()
+{
+ std::unordered_map<X, Sum, Hash, Equal> amap;
+ amap.insert({{X{"abc"}, 1}, {X{"def"}, 2}, {X{"ghi"}, 3}});
+
+ VERIFY(Sum(3) == amap.at(Y{std::string_view{"def"}}));
+ VERIFY(amap.size() == 3);
+
+ amap.at(Y{std::string_view{"deg"}}) = Sum(4);
+ VERIFY(amap.size() == 4);
+
+ auto const& amapr{amap};
+ VERIFY(Sum(4) == amapr.at(Y{std::string_view{"deg"}}));
+ try
+ {
+ amapr.at(Y{std::string_view{"deh"}});
+ VERIFY(false); // Should have thrown.
+ }
+ catch (std::out_of_range&) { VERIFY(amapr.size() == 4); }
+ catch (...) { VERIFY(false); } // Wrong exception.
+}
+
+void test3() // try_emplace
+{
+ std::unordered_map<X, Sum, Hash, Equal> amap;
+ amap.insert({{X{"abc"}, 1}, {X{"def"}, 2}, {X{"ghi"}, 3}});
+
+ { // Fail, already there
+ auto [it, res] = amap.try_emplace(Y{std::string_view{"def"}}, 4, 5);
+ VERIFY(!res);
+ VERIFY(amap.size() == 3);
+ }
+ { // Succeed
+ auto [it, res] = amap.try_emplace(Y{std::string_view{"deg"}}, 4, 5);
+ VERIFY(res && it->second == Sum(9));
+ VERIFY(amap.size() == 4);
+ }
+ { // Hinted, fail
+ auto it = amap.try_emplace(amap.begin(), Y{std::string_view{"def"}}, 4, 5);
+ VERIFY(amap.size() == 3);
+ VERIFY(it->first == X("def"));
+ VERIFY(it->second == Sum(3));
+ }
+ { // Hinted, succeed
+ auto it = amap.try_emplace(amap.begin(), Y{std::string_view{"deg"}}, 4, 5);
+ VERIFY(amap.size() == 4);
+ VERIFY(it->first == X("deg"));
+ VERIFY(it->second == Sum(9));
+ }
+}
+
+void test4() // insert_or_assign
+{
+ std::unordered_map<X, Sum, Hash, Equal> amap;
+ amap.insert({{X{"abc"}, 1}, {X{"def"}, 2}, {X{"ghi"}, 3}});
+
+ { // Fail
+ auto [it, res] = amap.insert_or_assign(Y{std::string_view{"def"}}, 4);
+ VERIFY(!res);
+ VERIFY(amap.size() == 3);
+ VERIFY(it->second == 4);
+ }
+ { // Succeed
+ auto [it, res] = amap.insert_or_assign(Y{std::string{"deg"}}, 5);
+ VERIFY(res);
+ VERIFY(amap.size() == 4);
+ VERIFY(it->second == 5);
+ }
+
+ { // Hinted, fail
+ auto it = amap.insert_or_assign(amap.begin(), Y{std::string{"def"}}, 6);
+ VERIFY(amap.size() == 4);
+ VERIFY(it->second == 4);
+ }
+ { // Hinted, succeed
+ auto it = amap.insert_or_assign(amap.begin(), Y{std::string{"deh"}}, 7);
+ VERIFY(amap.size() == 5);
+ VERIFY(it->second == 7);
+ }
+}
+
+void test5() // bucket
+{
+ std::unordered_map<X, Sum, Hash, Equal> amap;
+ amap.insert({{X{"abc"}, 1}, {X{"def"}, 2}, {X{"ghi"}, 3}});
+
+ auto const& amapr{amap};
+ VERIFY(amapr.bucket(X{"def"}) == amapr.bucket(Y{std::string_view{"def"}}));
+}
+
+int main()
+{
+ test1();
+ test2();
+ test3();
+ test4();
+ test5();
+}
diff --git
a/libstdc++-v3/testsuite/23_containers/unordered_multimap/modifiers/hetero/insert.cc
b/libstdc++-v3/testsuite/23_containers/unordered_multimap/modifiers/hetero/insert.cc
new file mode 100644
index 00000000000..d67fcc05d60
--- /dev/null
+++
b/libstdc++-v3/testsuite/23_containers/unordered_multimap/modifiers/hetero/insert.cc
@@ -0,0 +1,44 @@
+// { dg-do compile { target c++26 } }
+
+#include <unordered_map>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <functional>
+#include <testsuite_hooks.h>
+
+struct X {
+ std::string s;
+ friend bool operator==(X const& a, X const& b) = default;
+};
+
+struct Y {
+ std::string_view s;
+ Y(std::string_view sv) : s(sv) {}
+ Y(std::string const& sv) : s(sv) {}
+ operator X() const { return X(std::basic_string(s)); }
+ friend bool operator==(Y const& a, Y const& b) = default;
+ friend bool operator==(X const& a, Y const& b) { return a.s == b.s; }
+};
+
+struct Hash {
+ using is_transparent = void;
+ template <typename T>
+ auto operator()(T const& t) const { return
std::hash<decltype(T::s)>{}(t.s); }
+};
+
+using Equal = std::equal_to<void>;
+
+void test1() // bucket
+{
+ std::unordered_multimap<X, int, Hash, Equal> amap;
+ amap.insert({{X{"abc"}, 1}, {X{"def"}, 2}, {X{"def"}, 3}, {X{"ghi"}, 3}});
+
+ auto const& amapr{amap};
+ VERIFY(amapr.bucket(X{"def"}) == amapr.bucket(Y{std::string{"def"}}));
+}
+
+int main()
+{
+ test1();
+}
diff --git
a/libstdc++-v3/testsuite/23_containers/unordered_multiset/modifiers/hetero/insert.cc
b/libstdc++-v3/testsuite/23_containers/unordered_multiset/modifiers/hetero/insert.cc
new file mode 100644
index 00000000000..b678bde56fc
--- /dev/null
+++
b/libstdc++-v3/testsuite/23_containers/unordered_multiset/modifiers/hetero/insert.cc
@@ -0,0 +1,44 @@
+// { dg-do compile { target c++26 } }
+
+#include <unordered_set>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <functional>
+#include <testsuite_hooks.h>
+
+struct X {
+ std::string s;
+ friend bool operator==(X const& a, X const& b) = default;
+};
+
+struct Y {
+ std::string_view s;
+ Y(std::string_view sv) : s(sv) {}
+ Y(std::string const& sv) : s(sv) {}
+ operator X() const { return X(std::basic_string(s)); }
+ friend bool operator==(Y const& a, Y const& b) = default;
+ friend bool operator==(X const& a, Y const& b) { return a.s == b.s; }
+};
+
+struct Hash {
+ using is_transparent = void;
+ template <typename T>
+ auto operator()(T const& t) const { return
std::hash<decltype(T::s)>{}(t.s); }
+};
+
+using Equal = std::equal_to<void>;
+
+void test1() // bucket
+{
+ std::unordered_multiset<X, Hash, Equal> aset{};
+ aset.insert({X{"abc"}, X{"def"}, X{"def"}, X{"ghi"}});
+
+ auto const& asetr{aset};
+ VERIFY(asetr.bucket(X{"def"}) == asetr.bucket(Y{std::string{"def"}}));
+}
+
+int main()
+{
+ test1();
+}
diff --git
a/libstdc++-v3/testsuite/23_containers/unordered_set/modifiers/hetero/insert.cc
b/libstdc++-v3/testsuite/23_containers/unordered_set/modifiers/hetero/insert.cc
new file mode 100644
index 00000000000..2c5bd583cb9
--- /dev/null
+++
b/libstdc++-v3/testsuite/23_containers/unordered_set/modifiers/hetero/insert.cc
@@ -0,0 +1,75 @@
+// { dg-do compile { target c++26 } }
+
+#include <unordered_set>
+#include <string>
+#include <string_view>
+#include <utility>
+#include <functional>
+#include <testsuite_hooks.h>
+
+struct X {
+ std::string s;
+ friend bool operator==(X const& a, X const& b) = default;
+};
+
+struct Y {
+ std::string_view s;
+ Y(std::string_view sv) : s(sv) {}
+ Y(std::string const& sv) : s(sv) {}
+ operator X() const { return X(std::basic_string(s)); }
+ friend bool operator==(Y const& a, Y const& b) = default;
+ friend bool operator==(X const& a, Y const& b) { return a.s == b.s; }
+};
+
+struct Hash {
+ using is_transparent = void;
+ template <typename T>
+ auto operator()(T const& t) const { return
std::hash<decltype(T::s)>{}(t.s); }
+};
+
+using Equal = std::equal_to<void>;
+
+void test1() // insert
+{
+ std::unordered_set<X, Hash, Equal> aset;
+ aset.insert({X{"abc"}, X{"def"}, X{"ghi"}});
+
+ { // Fail
+ auto [it, res] = aset.insert(Y{std::string{"def"}});
+ VERIFY(!res);
+ VERIFY(aset.size() == 3);
+ VERIFY(it->s == std::string{"def"});
+ }
+ { // Succeed
+ auto [it, res] = aset.insert(Y{std::string{"deg"}});
+ VERIFY(res);
+ VERIFY(aset.size() == 4);
+ VERIFY(it->s == std::string{"deg"});
+ }
+
+ { // Hinted, fail
+ auto it = aset.insert(aset.begin(), Y{std::string{"def"}});
+ VERIFY(aset.size() == 4);
+ VERIFY(it->s == std::string{"def"});
+ }
+ { // Hinted, succeed
+ auto it = aset.insert(aset.begin(), Y{std::string{"deh"}});
+ VERIFY(aset.size() == 5);
+ VERIFY(it->s == std::string{"deh"});
+ }
+}
+
+void test2() // bucket
+{
+ std::unordered_set<X, Hash, Equal> aset{};
+ aset.insert({X{"abc"}, X{"def"}, X{"ghi"}});
+
+ auto const& asetr{aset};
+ VERIFY(asetr.bucket(X{"def"}) == asetr.bucket(Y{std::string{"def"}}));
+}
+
+int main()
+{
+ test1();
+ test2();
+}
--
2.52.0