[gcc r17-4086] libstdc++: Value-initialize _Hashtable_ebo_helper members [PR126949]
Tomasz Kaminski
tkaminsk@gcc.gnu.org
Thu Sep 10 13:38:17 GMT 2026
https://gcc.gnu.org/g:e36fef054c03112a4cd20a6ff0a3b3533c41f2b5
commit r17-4086-ge36fef054c03112a4cd20a6ff0a3b3533c41f2b5
Author: Tomasz Kamiński <tkaminsk@redhat.com>
Date: Thu Sep 10 11:20:45 2026 +0200
libstdc++: Value-initialize _Hashtable_ebo_helper members [PR126949]
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, aggregate initialization is performed.
In particular such initialization is ill-formed, if any aggregate member
has explicit default constructor, as they cannot be initialized from {}.
This introduced an regression, where hasher, equality, and allocators with
explicit default constructor were no longer supported.
This patch addresses above by properly value-initializing such members,
using = Type() syntax, which in turn value-initializes all aggregate
members, and calls 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.
Reviewed-by: Jonathan Wakely <jwakely@redhat.com>
Co-authored-by: Marco Falke <falke.marco@gmail.com>
Signed-off-by: Tomasz Kamiński <tkaminsk@redhat.com>
Diff:
---
libstdc++-v3/include/bits/hashtable_policy.h | 9 ++--
.../23_containers/unordered_map/cons/default.cc | 49 ++++++++++++++++++++++
.../23_containers/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 6e4b365d5925..84ebbf65bcb8 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 7a785e980b1f..2a65253c240c 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 fb87c96ce9d4..ae12afca797a 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, ExplicitHash> set1;
+ std::unordered_set<int, std::hash<int>, ExplicitEqual> set2;
+ std::unordered_set<int, std::hash<int>, std::equal_to<int>,
+ ExplicitAlloc<int>> set3;
+ std::unordered_set<int, ExplicitHash, ExplicitEqual,
+ ExplicitAlloc<int>> set4;
+}
More information about the Libstdc++-cvs
mailing list