On Thu, Sep 10, 2026 at 10:42 AM Tomasz Kaminski <[email protected]> wrote:
> > > On Mon, Aug 31, 2026 at 5:58 PM Marco Falke <[email protected]> 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 <[email protected]> >> 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 >> >
