On Thu, 19 Feb 2026 at 16:56, François Dumont <[email protected]> wrote:
>
>
> On 2/19/26 13:04, Jonathan Wakely wrote:
> > On Thu, 12 Feb 2026 at 18:12, Nathan Myers <[email protected]> wrote:
> >> Changes in v2:
> >> - Fix numerous errors revealed by actual testing.
> >> - Delete excess overloads in unordered_multimap and
> >> unordered_multiset.
> >>
> >> Implement the debug versions of new overloads from P2363.
> >>
> >> Also, simplify implementation of other overloads to match.
> >>
> >> libstdc++-v3/ChangeLog:
> >> PR libstdc++/117402
> >> * include/debug/map.h (try_emplace (2x), insert_or_assign (2x)):
> >> Define heterogeneous overloads, simplify existing overloads.
> >> * include/debug/unordered_map: Same.
> >> * include/debug/set.h (insert (2x)):
> >> Define heterogeneous overloads.
> >> * include/debug/unordered_set: Same.
> >> ---
> >> libstdc++-v3/include/debug/map.h | 83 +++++++++++++++++-------
> >> libstdc++-v3/include/debug/set.h | 21 ++++++
> >> libstdc++-v3/include/debug/unordered_map | 64 +++++++++++++++---
> >> libstdc++-v3/include/debug/unordered_set | 26 ++++++++
> >> 4 files changed, 161 insertions(+), 33 deletions(-)
> >>
> >> diff --git a/libstdc++-v3/include/debug/map.h
> >> b/libstdc++-v3/include/debug/map.h
> >> index 0fc7afae385..d6d2600d904 100644
> >> --- a/libstdc++-v3/include/debug/map.h
> >> +++ b/libstdc++-v3/include/debug/map.h
> >> @@ -382,18 +382,26 @@ namespace __debug
> >> return { { __res.first, this }, __res.second };
> >> }
> >>
> >> +# ifdef __glibcxx_associative_heterogeneous_insertion
> >> + template <__heterogeneous_tree_key<map> _Kt, typename... _Args>
> >> + pair<iterator, bool>
> >> + try_emplace(_Kt&& __k, _Args&&... __args)
> >> + {
> >> + auto __res = _Base::try_emplace(
> >> + std::forward<_Kt>(__k), std::forward<_Args>(__args)...);
> >> + return { { __res.first, this }, __res.second };
> >> + }
> >> +#endif
> >> +
> >> template <typename... _Args>
> >> iterator
> >> try_emplace(const_iterator __hint, const key_type& __k,
> >> _Args&&... __args)
> >> {
> >> __glibcxx_check_insert(__hint);
> >> - return
> >> - {
> >> - _Base::try_emplace(__hint.base(), __k,
> >> - std::forward<_Args>(__args)...),
> >> - this
> >> - };
> >> + auto __it = _Base::try_emplace(__hint.base(), __k,
> >> + std::forward<_Args>(__args)...);
> >> + return { __it, this };
> >> }
> >>
> >> template <typename... _Args>
> >> @@ -401,14 +409,23 @@ namespace __debug
> >> try_emplace(const_iterator __hint, key_type&& __k, _Args&&...
> >> __args)
> >> {
> >> __glibcxx_check_insert(__hint);
> >> - return
> >> - {
> >> - _Base::try_emplace(__hint.base(), std::move(__k),
> >> - std::forward<_Args>(__args)...),
> >> - this
> >> - };
> >> + auto __it = _Base::try_emplace(__hint.base(), std::move(__k),
> >> + std::forward<_Args>(__args)...);
> >> + return { __it, this };
> >> }
> >>
> >> +# ifdef __glibcxx_associative_heterogeneous_insertion
> >> + template <__heterogeneous_tree_key<map> _Kt, typename... _Args>
> >> + iterator
> >> + try_emplace(const_iterator __hint, _Kt&& __k, _Args&&... __args)
> >> + {
> >> + __glibcxx_check_insert(__hint);
> >> + auto __it = _Base::try_emplace(__hint.base(),
> >> + std::forward<_Kt>(__k), std::forward<_Args>(__args)...);
> >> + return { __it, this };
> >> + }
> >> +# endif
> >> +
> >> template <typename _Obj>
> >> std::pair<iterator, bool>
> >> insert_or_assign(const key_type& __k, _Obj&& __obj)
> >> @@ -427,18 +444,26 @@ namespace __debug
> >> return { { __res.first, this }, __res.second };
> >> }
> >>
> >> +# ifdef __glibcxx_associative_heterogeneous_insertion
> >> + template <__heterogeneous_tree_key<map> _Kt, typename _Obj>
> >> + std::pair<iterator, bool>
> >> + insert_or_assign(_Kt&& __k, _Obj&& __obj)
> >> + {
> >> + auto __res = _Base::insert_or_assign(
> >> + std::forward<_Kt>(__k), std::forward<_Obj>(__obj));
> >> + return { { __res.first, this }, __res.second };
> >> + }
> >> +#endif
> >> +
> >> template <typename _Obj>
> >> iterator
> >> insert_or_assign(const_iterator __hint,
> >> const key_type& __k, _Obj&& __obj)
> >> {
> >> __glibcxx_check_insert(__hint);
> >> - return
> >> - {
> >> - _Base::insert_or_assign(__hint.base(), __k,
> >> - std::forward<_Obj>(__obj)),
> >> - this
> >> - };
> >> + auto __it = _Base::insert_or_assign(__hint.base(), __k,
> >> + std::forward<_Obj>(__obj));
> >> + return { __it, this };
> >> }
> >>
> >> template <typename _Obj>
> >> @@ -446,13 +471,23 @@ namespace __debug
> >> insert_or_assign(const_iterator __hint, key_type&& __k, _Obj&&
> >> __obj)
> >> {
> >> __glibcxx_check_insert(__hint);
> >> - return
> >> - {
> >> - _Base::insert_or_assign(__hint.base(), std::move(__k),
> >> - std::forward<_Obj>(__obj)),
> >> - this
> >> - };
> >> + auto __it = _Base::insert_or_assign(__hint.base(),
> >> std::move(__k),
> >> + std::forward<_Obj>(__obj));
> >> + return { __it, this };
> >> }
> >> +
> >> +# ifdef __glibcxx_associative_heterogeneous_insertion
> >> + template <__heterogeneous_tree_key<map> _Kt, typename _Obj>
> >> + iterator
> >> + insert_or_assign(const_iterator __hint, _Kt&& __k, _Obj&& __obj)
> >> + {
> >> + __glibcxx_check_insert(__hint);
> >> + auto __it = _Base::insert_or_assign(__hint.base(),
> >> + std::forward<_Kt>(__k), std::forward<_Obj>(__obj));
> >> + return { __it, this };
> >> + }
> >> +# endif
> >> +
> >> #endif // C++17
> >>
> >> #ifdef __glibcxx_node_extract // >= C++17 && HOSTED
> >> diff --git a/libstdc++-v3/include/debug/set.h
> >> b/libstdc++-v3/include/debug/set.h
> >> index 99701d4a6d7..9180a87285b 100644
> >> --- a/libstdc++-v3/include/debug/set.h
> >> +++ b/libstdc++-v3/include/debug/set.h
> >> @@ -283,6 +283,16 @@ namespace __debug
> >> }
> >> #endif
> >>
> >> +#ifdef __glibcxx_associative_heterogeneous_insertion
> >> + template <__heterogeneous_tree_key<set> _Kt>
> >> + std::pair<iterator, bool>
> >> + insert(_Kt&& __x)
> >> + {
> >> + auto __res = _Base::insert(std::forward<_Kt>(__x));
> >> + return { { __res.first, this }, __res.second };
> >> + }
> >> +#endif
> >> +
> >> iterator
> >> insert(const_iterator __position, const value_type& __x)
> >> {
> >> @@ -299,6 +309,17 @@ namespace __debug
> >> }
> >> #endif
> >>
> >> +#ifdef __glibcxx_associative_heterogeneous_insertion
> >> + template <__heterogeneous_tree_key<set> _Kt>
> >> + iterator
> >> + insert(const_iterator __position, _Kt&& __x)
> >> + {
> >> + __glibcxx_check_insert(__position);
> >> + auto __it = _Base::insert(__position.base(),
> >> std::forward<_Kt>(__x));
> >> + return { __it, this };
> >> + }
> >> +#endif
> >> +
> >> template <typename _InputIterator>
> >> void
> >> insert(_InputIterator __first, _InputIterator __last)
> >> diff --git a/libstdc++-v3/include/debug/unordered_map
> >> b/libstdc++-v3/include/debug/unordered_map
> >> index 4bde18c917b..1dda76dcf32 100644
> >> --- a/libstdc++-v3/include/debug/unordered_map
> >> +++ b/libstdc++-v3/include/debug/unordered_map
> >> @@ -500,6 +500,17 @@ namespace __debug
> >> return { { __res.first, this }, __res.second };
> >> }
> >>
> >> +# ifdef __glibcxx_associative_heterogeneous_insertion
> >> + template <__heterogeneous_hash_key<unordered_map> _Kt, typename...
> >> _Args>
> >> + pair<iterator, bool>
> >> + try_emplace(_Kt&& __k, _Args&&... __args)
> >> + {
> >> + auto __res = _Base::try_emplace(std::forward<_Kt>(__k),
> >> + std::forward<_Args>(__args)...);
> >> + return { { __res.first, this }, __res.second };
> >> + }
> >> +# endif
> >> +
> >> template <typename... _Args>
> >> iterator
> >> try_emplace(const_iterator __hint, const key_type& __k,
> >> @@ -516,10 +527,21 @@ namespace __debug
> >> try_emplace(const_iterator __hint, key_type&& __k, _Args&&...
> >> __args)
> >> {
> >> __glibcxx_check_insert(__hint);
> >> - return { _Base::try_emplace(__hint.base(), std::move(__k),
> >> - std::forward<_Args>(__args)...),
> >> - this };
> >> + auto __it = _Base::try_emplace(__hint.base(), std::move(__k),
> >> + std::forward<_Args>(__args)...);
> >> + return { __it, this };
> >
> > Are all these try_emplace overloads missing calls to _M_check_rehashed?
>
> Yes, indeed.
>
> _M_check_rehashed is also called in erase operations even if currently
> we never rehash on erasure, even in tr1 implementation by the way.
>
> Maybe it's done this way just in case we start rehash on erasure one
> day. But it's already inconsistent then as erase of an iterator range is
> not doing the same.
The standard does not allow us to rehash on erasure.
>
>
> > I think I should have added those in r6-2989-g66c182be120bb3 when
> > adding try_emplace to the debug maps.
> > But that's a pre-existing problem that can be solved separately.
> >
> > This is OK for trunk (after the main P2363 patch is pushed, of course).
> >
> >
> >
> >
> >
> >
> >> }
> >> +# ifdef __glibcxx_associative_heterogeneous_insertion
> >> + template <__heterogeneous_hash_key<unordered_map> _Kt, typename...
> >> _Args>
> >> + iterator
> >> + try_emplace(const_iterator __hint, _Kt&& __k, _Args&&... __args)
> >> + {
> >> + __glibcxx_check_insert(__hint);
> >> + auto __it = _Base::try_emplace(__hint.base(),
> >> + std::forward<_Kt>(__k), std::forward<_Args>(__args)...);
> >> + return { __it, this };
> >> + }
> >> +# endif
> >>
> >> template <typename _Obj>
> >> pair<iterator, bool>
> >> @@ -539,15 +561,26 @@ namespace __debug
> >> return { { __res.first, this }, __res.second };
> >> }
> >>
> >> +# ifdef __glibcxx_associative_heterogeneous_insertion
> >> + template <__heterogeneous_hash_key<unordered_map> _Kt, typename
> >> _Obj>
> >> + pair<iterator, bool>
> >> + insert_or_assign(_Kt&& __k, _Obj&& __obj)
> >> + {
> >> + auto __res = _Base::insert_or_assign(
> >> + std::forward<_Kt>(__k), std::forward<_Obj>(__obj));
> >> + return { { __res.first, this }, __res.second };
> >> + }
> >> +# endif
> >> +
> >> template <typename _Obj>
> >> iterator
> >> insert_or_assign(const_iterator __hint, const key_type& __k,
> >> _Obj&& __obj)
> >> {
> >> __glibcxx_check_insert(__hint);
> >> - return { _Base::insert_or_assign(__hint.base(), __k,
> >> - std::forward<_Obj>(__obj)),
> >> - this };
> >> + auto __it = _Base::insert_or_assign(__hint.base(), __k,
> >> + std::forward<_Obj>(__obj));
> >> + return { __it, this };
> >> }
> >>
> >> template <typename _Obj>
> >> @@ -555,10 +588,23 @@ namespace __debug
> >> insert_or_assign(const_iterator __hint, key_type&& __k, _Obj&&
> >> __obj)
> >> {
> >> __glibcxx_check_insert(__hint);
> >> - return { _Base::insert_or_assign(__hint.base(), std::move(__k),
> >> - std::forward<_Obj>(__obj)),
> >> - this };
> >> + auto __it = _Base::insert_or_assign(__hint.base(),
> >> std::move(__k),
> >> + std::forward<_Obj>(__obj));
> >> + return { __it, this };
> >> }
> >> +
> >> +# ifdef __glibcxx_associative_heterogeneous_insertion
> >> + template <__heterogeneous_hash_key<unordered_map> _Kt, typename
> >> _Obj>
> >> + iterator
> >> + insert_or_assign(const_iterator __hint, _Kt&& __k, _Obj&& __obj)
> >> + {
> >> + __glibcxx_check_insert(__hint);
> >> + auto __it = _Base::insert_or_assign(__hint.base(),
> >> + std::forward<_Kt>(__k), std::forward<_Obj>(__obj));
> >> + return { __it, this };
> >> + }
> >> +# endif
> >> +
> >> #endif // C++17
> >>
> >> #ifdef __glibcxx_node_extract // >= C++17 && HOSTED
> >> diff --git a/libstdc++-v3/include/debug/unordered_set
> >> b/libstdc++-v3/include/debug/unordered_set
> >> index de999a76890..06430e7cf64 100644
> >> --- a/libstdc++-v3/include/debug/unordered_set
> >> +++ b/libstdc++-v3/include/debug/unordered_set
> >> @@ -413,6 +413,18 @@ namespace __debug
> >> return { { __res.first, this }, __res.second };
> >> }
> >>
> >> +# ifdef __glibcxx_associative_heterogeneous_insertion
> >> + template <__heterogeneous_hash_key<unordered_set> _Kt>
> >> + std::pair<iterator, bool>
> >> + insert(_Kt&& __obj)
> >> + {
> >> + size_type __bucket_count = this->bucket_count();
> >> + auto __res = _Base::insert(std::forward<_Kt>(__obj));
> >> + _M_check_rehashed(__bucket_count);
> >> + return { { __res.first, this }, __res.second };
> >> + }
> >> +#endif
> >> +
> >> iterator
> >> insert(const_iterator __hint, value_type&& __obj)
> >> {
> >> @@ -423,6 +435,20 @@ namespace __debug
> >> return { __it, this };
> >> }
> >>
> >> +# ifdef __glibcxx_associative_heterogeneous_insertion
> >> + template <__heterogeneous_hash_key<unordered_set> _Kt>
> >> + iterator
> >> + insert(const_iterator __hint, _Kt&& __obj)
> >> + {
> >> + __glibcxx_check_insert(__hint);
> >> + size_type __bucket_count = this->bucket_count();
> >> + auto __it = _Base::insert(
> >> + __hint.base(), std::forward<_Kt>(__obj));
> >> + _M_check_rehashed(__bucket_count);
> >> + return { __it, this };
> >> + }
> >> +#endif
> >> +
> >> void
> >> insert(std::initializer_list<value_type> __l)
> >> {
> >> --
> >> 2.52.0
> >>