[PATCH] libstdc++: Value-initialize hashtable helper objects [PR126949]

Tomasz Kaminski tkaminsk@redhat.com
Thu Sep 10 08:53:40 GMT 2026


On Thu, Sep 10, 2026 at 10:42 AM Tomasz Kaminski <tkaminsk@redhat.com>
wrote:

>
>
> On Mon, Aug 31, 2026 at 5:58 PM Marco Falke <falke.marco@gmail.com> wrote:
>
>> libstdc++15 and 16 do not allow to construct an unordered set with a
>> hasher whose ctor is explicit.
>>
>> This is an issue with Clang, or with GCC -Wsystem-headers. See
>> https://godbolt.org/z/EGzKbjh84
>>
>> Fix this by using value-init, similar to commit
>> f8f0193b5b83f6e85d65015e79c803295baf5166. However, that likely
>> regressed again in commit 689d4abc0b836aaf1c8ecd38091dde6b14373c60?
>>
>> patch is attached and copied later.
>>
>> I'd say the unit test could be expanded by adding
>> // { dg-additional-options "-Werror -Wsystem-headers" }
>> , but I wasn't sure if this is recommended.
>>
>> Best, Marco
>>
>>
>>
>>
>> From 2bd4a6218ff9ca10f1d11b6b19411c9556df90ed Mon Sep 17 00:00:00 2001
>> From: MarcoFalke <falke.marco@gmail.com>
>> Date: Sun, 26 Jul 2026 14:30:58 +0000
>> Subject: [PATCH] libstdc++: Value-initialize hashtable helper objects
>>  [PR126949]
>>
>> Value-initialize the object stored in _Hashtable_ebo_helper so that hash
>> containers can be default constructed when the hash function has an
>> explicit default constructor.
>>
>> A default member initializer prevents aggregate initialization in C++11,
>> so add constructors that preserve initialization from hash, equality, and
>> allocator objects.
>>
>> libstdc++-v3/ChangeLog:
>>
>> PR libstdc++/126949
>> * include/bits/hashtable_policy.h (_Hashtable_ebo_helper):
>> Value-initialize the stored object. Add default and forwarding
>> constructors.
>> * testsuite/23_containers/unordered_map/cons/default.cc: Check
>> default construction with an explicit hash constructor.
>> * testsuite/23_containers/unordered_set/cons/default.cc: Likewise.
>> ---
>>  libstdc++-v3/include/bits/hashtable_policy.h   | 18 ++++++++++++++++--
>>  .../unordered_map/cons/default.cc              | 15 +++++++++++++++
>>  .../unordered_set/cons/default.cc              | 15 +++++++++++++++
>>  3 files changed, 46 insertions(+), 2 deletions(-)
>>
>> diff --git a/libstdc++-v3/include/bits/hashtable_policy.h
>> b/libstdc++-v3/include/bits/hashtable_policy.h
>> index 6e4b365d592..396214ed4bb 100644
>> --- a/libstdc++-v3/include/bits/hashtable_policy.h
>> +++ b/libstdc++-v3/include/bits/hashtable_policy.h
>> @@ -1033,7 +1033,14 @@ namespace __detail
>>     bool __use_ebo = !__is_final(_Tp) && __is_empty(_Tp)>
>>      struct _Hashtable_ebo_helper
>>      {
>> -      [[__no_unique_address__]] _Tp _M_obj;
>> +      [[__no_unique_address__]] _Tp _M_obj{};
>>
> This is not value-initialization, but aggregate initialization (there are
> observable
> differences in corner cases). I would prefer that we actually use an
> value-init
> here and say = _Tp().
>
Integerestingly your example actually hit the case where there is
difference in behavior between
{} and = Tp(), in the defintion of _M_hash of _Hash_code_base. I believe
the proper fix is to change:
[[__no_unique_address__]] _Hashtable_ebo_helper<_Hash> _M_hash{};
To
[[__no_unique_address__]] _Hashtable_ebo_helper<_Hash> _M_hash =
_Hashtable_ebo_helper<_Hash>();

We should do the same for _M_equal and _M_alloc.


> +
>> +      _Hashtable_ebo_helper() = default;
>>
> This constructor will no longer be trivial if the _Tp has trivial
> constructor, so
> I need to take a look to see if that may have ABI impact.
>
>> +
>> +      template<typename _Up>
>> + _Hashtable_ebo_helper(_Up&& __obj)
>> + : _M_obj(std::forward<_Up>(__obj))
>> + { }
>>      };
>>
>>  #if ! _GLIBCXX_INLINE_VERSION
>> @@ -1042,7 +1049,14 @@ namespace __detail
>>    template<typename _Tp>
>>      struct _Hashtable_ebo_helper<_Tp, false>
>>      {
>> -      _Tp _M_obj;
>> +      _Tp _M_obj{};
>> +
>> +      _Hashtable_ebo_helper() = default;
>> +
>> +      template<typename _Up>
>> + _Hashtable_ebo_helper(_Up&& __obj)
>> + : _M_obj(std::forward<_Up>(__obj))
>> + { }
>>      };
>>  #endif
>>
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/unordered_map/cons/default.cc
>> b/libstdc++-v3/testsuite/23_containers/unordered_map/cons/default.cc
>> index 7a785e980b1..07089c36bcf 100644
>> --- a/libstdc++-v3/testsuite/23_containers/unordered_map/cons/default.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/unordered_map/cons/default.cc
>> @@ -49,3 +49,18 @@ Map4 m4{m};
>>  Map4 m5{m, a};
>>  Map4 m6{std::move(m)};
>>  Map4 m7{std::move(m6), a};
>> +
>> +// PR libstdc++/126949
>> +struct ExplicitHash
>> +{
>> +  explicit ExplicitHash(bool = false) { }
>> +
>> +  std::size_t operator()(int value) const
>> +  { return value; }
>> +};
>> +
>> +void
>> +test_explicit_hash()
>> +{
>> +  std::unordered_map<int, int, ExplicitHash> map;
>> +}
>> diff --git
>> a/libstdc++-v3/testsuite/23_containers/unordered_set/cons/default.cc
>> b/libstdc++-v3/testsuite/23_containers/unordered_set/cons/default.cc
>> index fb87c96ce9d..8949b202125 100644
>> --- a/libstdc++-v3/testsuite/23_containers/unordered_set/cons/default.cc
>> +++ b/libstdc++-v3/testsuite/23_containers/unordered_set/cons/default.cc
>> @@ -48,3 +48,18 @@ Set4 s4{s};
>>  Set4 s5{s, a};
>>  Set4 s6{std::move(s)};
>>  Set4 s7{std::move(s6), a};
>> +
>> +// PR libstdc++/126949
>> +struct ExplicitHash
>> +{
>> +  explicit ExplicitHash(bool = false) { }
>> +
>> +  std::size_t operator()(int value) const
>> +  { return value; }
>> +};
>> +
>> +void
>> +test_explicit_hash()
>> +{
>> +  std::unordered_set<int, ExplicitHash> set;
>> +}
>> --
>> 2.53.0
>>
>
-------------- next part --------------
An HTML attachment was scrubbed...
URL: <https://gcc.gnu.org/pipermail/libstdc++/attachments/20260910/d0017936/attachment.htm>


More information about the Libstdc++ mailing list