[PATCH v2] libstdc++: Value-initialize _Hashtable_ebo_helper member [PR126949]

Tomasz Kamiński tkaminsk@redhat.com
Thu Sep 10 09:38:33 GMT 2026


The no_unique_address _Hashtable_ebo_helper members (that replaced
base classes in r15-6273-g689d4abc0b836a), are initialized with {}.
Since the _Hashtable_ebo_helper are aggregates, the "{}" was performing
aggregate initializtion, and not value initialization. In particular such
aggregate initialization is ill-formed, if members has explicit default
constructor, as they cannot be initialized from {} in such case.
This introduced and regression, when hasher, equality and alloators
with explict default constructor are no longer supported.

This patch addreses above by properly value-initializing such members,
using = Type() syntax, which in turn value-initializes all members,
and accepts explicit default constructors.

libstdc++-v3/ChangeLog:

	PR libstdc++/126949
	* include/bits/hashtable_policy.h (_Hash_code_base::_M_hash)
	(_Hashtable_base::_M_equal, _Hashtable_alloc::_M_alloc):
	Value initialized members in NSDMI.
	* testsuite/23_containers/unordered_map/cons/default.cc: Check
	default construction with an hash, equality and allocator with
	explicit constructor.
	* testsuite/23_containers/unordered_set/cons/default.cc: Likewise.

Co-authored-by: MarcoFalke <falke.marco@gmail.com>
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
---
v2:
 - changes approach to fix, by value initializing _Hashtable_ebo_helper
 - expands test to cover equality and allocator with explicit default
   constructor

Testing on x86_64_linux. Tested modified test with all standard modes,
debug, assertions and -m32. OK for trunk/16/15 when all test passes?

 libstdc++-v3/include/bits/hashtable_policy.h  |  9 ++--
 .../unordered_map/cons/default.cc             | 49 +++++++++++++++++++
 .../unordered_set/cons/default.cc             | 48 ++++++++++++++++++
 3 files changed, 103 insertions(+), 3 deletions(-)

diff --git a/libstdc++-v3/include/bits/hashtable_policy.h b/libstdc++-v3/include/bits/hashtable_policy.h
index 6e4b365d592..84ebbf65bcb 100644
--- a/libstdc++-v3/include/bits/hashtable_policy.h
+++ b/libstdc++-v3/include/bits/hashtable_policy.h
@@ -1074,7 +1074,8 @@ namespace __detail
       { return _M_hash._M_obj; }
 
     protected:
-      [[__no_unique_address__]] _Hashtable_ebo_helper<_Hash> _M_hash{};
+      [[__no_unique_address__]]
+      _Hashtable_ebo_helper<_Hash> _M_hash = _Hashtable_ebo_helper<_Hash>();
 
       using __hash_code = size_t;
 
@@ -1411,7 +1412,8 @@ namespace __detail
       using __hash_code = typename __hash_code_base::__hash_code;
 
     protected:
-      [[__no_unique_address__]] _Hashtable_ebo_helper<_Equal> _M_equal{};
+      [[__no_unique_address__]]
+      _Hashtable_ebo_helper<_Equal> _M_equal = _Hashtable_ebo_helper<_Equal>();
 
       _Hashtable_base() = default;
 
@@ -1491,7 +1493,8 @@ namespace __detail
     struct _Hashtable_alloc
     {
     private:
-      [[__no_unique_address__]] _Hashtable_ebo_helper<_NodeAlloc> _M_alloc{};
+      [[__no_unique_address__]]
+      _Hashtable_ebo_helper<_NodeAlloc> _M_alloc = _Hashtable_ebo_helper<_NodeAlloc>();
 
       template<typename>
 	struct __get_value_type;
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..2a65253c240 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,52 @@ 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; }
+};
+
+struct ExplicitEqual
+{
+  explicit ExplicitEqual(bool = false) { }
+
+  bool operator()(int lhs, int rhs) const
+  { return lhs == rhs; }
+};
+
+template<typename T>
+  struct ExplicitAlloc
+  {
+    using value_type = T;
+
+    explicit ExplicitAlloc(bool = false) noexcept { }
+
+    template<typename U>
+      ExplicitAlloc(const ExplicitAlloc<U>&) { }
+
+    T *allocate(std::size_t n)
+    { return std::allocator<T>().allocate(n); }
+
+    void deallocate(T *p, std::size_t n)
+    { std::allocator<T>().deallocate(p, n); }
+
+    bool operator==(const ExplicitAlloc&) const { return true; }
+    bool operator!=(const ExplicitAlloc&) const { return false; }
+  };
+
+void
+test_hash()
+{
+  std::unordered_map<int, int, ExplicitHash> map1;
+  std::unordered_map<int, int, std::hash<int>, ExplicitEqual> map2;
+  std::unordered_map<int, int, std::hash<int>, std::equal_to<int>,
+			       ExplicitAlloc<std::pair<const int, int>>> map3;
+  std::unordered_map<int, int, ExplicitHash, ExplicitEqual,
+			       ExplicitAlloc<std::pair<const int, int>>> map4;
+
+}
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..049b8059cdb 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,51 @@ 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; }
+};
+
+struct ExplicitEqual
+{
+  explicit ExplicitEqual(bool = false) { }
+
+  bool operator()(int lhs, int rhs) const
+  { return lhs == rhs; }
+};
+
+template<typename T>
+  struct ExplicitAlloc
+  {
+    using value_type = T;
+
+    explicit ExplicitAlloc(bool = false) noexcept { }
+
+    template<typename U>
+      ExplicitAlloc(const ExplicitAlloc<U>&) { }
+
+    T *allocate(std::size_t n)
+    { return std::allocator<T>().allocate(n); }
+
+    void deallocate(T *p, std::size_t n)
+    { std::allocator<T>().deallocate(p, n); }
+
+    bool operator==(const ExplicitAlloc&) const { return true; }
+    bool operator!=(const ExplicitAlloc&) const { return false; }
+  };
+
+void
+test_explicit_hash()
+{
+  std::unordered_set<int, int, ExplicitHash> set1;
+  std::unordered_set<int, int, std::hash<int>, ExplicitEqual> set2;
+  std::unordered_set<int, int, std::hash<int>, std::equal_to<int>,
+			       ExplicitAlloc<int>> set3;
+  std::unordered_set<int, int, ExplicitHash, ExplicitEqual,
+			       ExplicitAlloc<int>> set4;
+}
-- 
2.55.0



More information about the Libstdc++ mailing list