STD::_HASHTABLE(3cxx)
π NAME π
Hiii bestie, welcome to the (3cxx) page for std::_Hashtable< _Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits > β¨ She's the secret main-character engine hiding under all your unordered_* containers, and she has so many template parameters. It's giving "I contain multitudes" and honestly? She does. This is part 1, and we're going through every single thing, no skips. π
πΈ SYNOPSIS πΈ
First, the include, because obviously:
#include <hashtable.h>
And she does not come alone β she inherits from a whole friend group. All of them, in order:
std::__detail::_Hashtable_base< _Key, _Value, _ExtractKey, _Equal, _H1, _H2, _Hash, _Traits >std::__detail::_Map_base< _Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, _Unique_keys >std::__detail::_Insert< _Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, _Constant_iterators >std::__detail::_Rehash_base< _Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, typename >std::__detail::_Equality< _Key, _Value, _Alloc, _ExtractKey, _Equal, _H1, _H2, _Hash, _RehashPolicy, _Traits, _Unique_keys >std::__detail::_Hashtable_alloc< __alloc_rebind< _Alloc, __detail::_Hash_node< _Value, _Traits::__hash_cached::value > > >- and
std::_Enable_default_constructor< _Switch, _Tag >
That's her lineage. Ancestry dot com could never. π¦
π«Ά Public Types π«Ά
Her little type aliases, every single one, in order:
typedef _Alloc allocator_typeβ the allocator, the one holding the memory bag πusing const_iterator = typename __hashtable_base::const_iteratorβ the const iterator, freshly borrowed from__hashtable_baseusing const_local_iterator = typename __hashtable_base::const_local_iteratorβ const version of the local (per-bucket) iteratortypedef __value_alloc_traits::const_pointer const_pointerβ a const pointer, straight from__value_alloc_traitstypedef const value_type & const_referenceβ a const reference to avalue_typeusing difference_type = typename __hashtable_base::difference_typeβ the difference type, inherited vibeusing iterator = typename __hashtable_base::iteratorβ the iterator, also from__hashtable_basetypedef _Equal key_equalβ her key-equality functor typetypedef _Key key_typeβ the key type, yes it's literally_Keyusing local_iterator = typename __hashtable_base::local_iteratorβ the local iterator (the bucket-specific one)typedef __value_alloc_traits::pointer pointerβ a pointer, from__value_alloc_traitstypedef value_type & referenceβ a plain reference to avalue_typeusing size_type = typename __hashtable_base::size_typeβ the size type, again from__hashtable_basetypedef _Value value_typeβ the value type, aka_Value
Periodt. That's the whole type roster. π
π Public Member Functions π
Okay this is the big one, and yes we're doing all of them. Buckle up bestie, grab a strawberry π.
The constructors:
_Hashtable (_Hashtable &&__ht) noexcept(_S_nothrow_move())β move constructor, and she'snoexceptexactly when_S_nothrow_move()says so_Hashtable (_Hashtable &&__ht, const allocator_type &__a) noexcept(_S_nothrow_move< __node_alloc_traits::_S_always_equal()>())β move constructor that also takes an allocator, with her own conditionalnoexcepttemplate<typename _InputIterator > _Hashtable (_InputIterator __f, _InputIterator __l, size_type __bkt_count_hint=0, const _H1 &__hf=_H1(), const key_equal &__eql=key_equal(), const allocator_type &__a=allocator_type())β iterator-range constructor with bucket hint, hash, equality and allocator all defaultedtemplate<typename _InputIterator > _Hashtable (_InputIterator __first, _InputIterator __last, size_type __bkt_count_hint, const _H1 &, const _H2 &, const _Hash &, const _Equal &, const _ExtractKey &, const allocator_type &)β the full-drip range constructor where nothing is defaulted_Hashtable (const _Hashtable &)β copy constructor, classic_Hashtable (const _Hashtable &, const allocator_type &)β copy constructor that also takes an allocator_Hashtable (const allocator_type &__a)β allocator-only constructor_Hashtable (initializer_list< value_type > __l, size_type __bkt_count_hint=0, const _H1 &__hf=_H1(), const key_equal &__eql=key_equal(), const allocator_type &__a=allocator_type())β theinitializer_listconstructor, with bucket hint + hash + equality + allocator all defaulted_Hashtable (size_type __bkt_count_hint, const _H1 &, const _H2 &, const _Hash &, const _Equal &, const _ExtractKey &, const allocator_type &)β bucket hint plus every functor, fully specified_Hashtable (size_type __bkt_count_hint, const _H1 &__hf=_H1(), const key_equal &__eql=key_equal(), const allocator_type &__a=allocator_type())β bucket hint with the defaulted hash, equality and allocator
Rehash policy access:
const _RehashPolicy & __rehash_policy () constβ hands you back the rehash policy, const-stylevoid __rehash_policy (const _RehashPolicy &__pol)β sets the rehash policy from__pol
Internal insert/emplace machinery (public-flavored):
template<typename... _Args> auto _M_emplace (const_iterator __hint, false_type, _Args &&... __args) -> iteratorβ emplace with a hint and afalse_type, returns aniteratortemplate<typename... _Args> auto _M_emplace (true_type, _Args &&... __args) -> pair< iterator, bool >β emplace with atrue_type, returns apair< iterator, bool >template<typename _Arg , typename _NodeGenerator > auto _M_insert (_Arg &&__v, const _NodeGenerator &__node_gen, true_type, size_type __n_elt) -> pair< iterator, bool >β insert with a node generator, atrue_typeand an element count, returnspair< iterator, bool >template<typename _Arg , typename _NodeGenerator > auto _M_insert (const_iterator __hint, _Arg &&__v, const _NodeGenerator &__node_gen, false_type) -> iteratorβ hinted insert with node generator andfalse_type, returnsiterator
Iterating (all the begins):
const_iterator begin () const noexceptβ constbegin,noexceptiterator begin () noexceptβ non-constbegin,noexceptlocal_iterator begin (size_type __bkt)βbeginfor a specific bucket__bktconst_local_iterator begin (size_type __bkt) constβ constbeginfor bucket__bkt
Bucket business:
size_type bucket (const key_type &__k) constβ which bucket does key__klive in?size_type bucket_count () const noexceptβ how many buckets total,noexceptsize_type bucket_size (size_type __bkt) constβ how big is bucket__bkt
Const-iteration starters:
const_iterator cbegin () const noexceptβ const begin, thecversionconst_local_iterator cbegin (size_type __bkt) constβ const local begin for bucket__bktconst_iterator cend () const noexceptβ const end,noexceptconst_local_iterator cend (size_type __bkt) constβ const local end for bucket__bkt
Clearing, counting, emplacing:
void clear () noexceptβ wipe it all,noexceptsize_type count (const key_type &__k) constβ how many times does__kshow uptemplate<typename... _Args> __ireturn_type emplace (_Args &&... __args)β emplace, returns__ireturn_typetemplate<typename... _Args> iterator emplace_hint (const_iterator __hint, _Args &&... __args)β emplace but with a__hint, returns aniteratorbool empty () const noexceptβ is she empty,noexcept
The ends:
const_iterator end () const noexceptβ const end,noexceptiterator end () noexceptβ non-const end,noexceptlocal_iterator end (size_type __bkt)β end for bucket__bktconst_local_iterator end (size_type __bkt) constβ const end for bucket__bkt
Lookups and ranges:
std::pair< iterator, iterator > equal_range (const key_type &__k)β the range where__klives, non-conststd::pair< const_iterator, const_iterator > equal_range (const key_type &__k) constβ the const version of that range
Erasing (four whole flavors):
size_type erase (const key_type &__k)β erase by key, tells you how many went byeiterator erase (const_iterator)β erase by const iterator, returns aniteratoriterator erase (const_iterator, const_iterator)β erase a whole const-iterator rangeiterator erase (iterator __it)β erase by iterator__it
Finding + properties:
iterator find (const key_type &__k)β find__k, non-constconst_iterator find (const key_type &__k) constβ find__k, constallocator_type get_allocator () const noexceptβ hand over the allocator,noexceptkey_equal key_eq () constβ hand over the key-equality functorfloat load_factor () const noexceptβ the load factor as afloat,noexceptsize_type max_bucket_count () const noexceptβ the max bucket count,noexceptsize_type max_size () const noexceptβ the max size,noexcept
Assignment operators:
_Hashtable & operator= (_Hashtable &&__ht) noexcept(__node_alloc_traits::_S_nothrow_move() &&is_nothrow_move_assignable< _H1 >::value &&is_nothrow_move_assignable< _Equal >::value)β move assignment,noexceptunder that exact condition_Hashtable & operator= (const _Hashtable &__ht)β copy assignment from__ht_Hashtable & operator= (initializer_list< value_type > __l)β assign from aninitializer_list__l
Resizing + the rest:
void rehash (size_type __bkt_count)β rehash into__bkt_countbucketssize_type size () const noexceptβ how many elements,noexceptvoid swap (_Hashtable &) noexcept(__and_< __is_nothrow_swappable< _H1 >, __is_nothrow_swappable< _Equal > >::value)β swap,noexceptexactly when__and_< __is_nothrow_swappable< _H1 >, __is_nothrow_swappable< _Equal > >::valueholds
π Protected Member Functions π
These are the behind-the-scenes girls β protected, so they're for the family only:
size_type _M_bucket_index (__node_type *__n) const noexceptβ bucket index from a node pointer,noexceptsize_type _M_bucket_index (const key_type &__k, __hash_code __c) constβ bucket index from key__kand hash code__ctemplate<typename... _Args> iterator _M_emplace (const_iterator, false_type, _Args &&... __args)β protected emplace, const-iterator +false_type, returnsiteratortemplate<typename... _Args> iterator _M_emplace (const_iterator, true_type __uk, _Args &&... __args)β protected emplace with const-iterator andtrue_type __uktemplate<typename... _Args> iterator _M_emplace (false_type __uk, _Args &&... __args)β protected emplace with justfalse_type __uktemplate<typename... _Args> std::pair< iterator, bool > _M_emplace (true_type, _Args &&... __args)β protected emplace withtrue_type, returnsstd::pair< iterator, bool >const _Equal & _M_eq () constβ hands back the equality functor, constbool _M_equals (const _Key &__k, __hash_code __c, __node_type *__n) constβ does__k(hash__c) equal node__n?size_type _M_erase (false_type, const key_type &)β erase path withfalse_type, returnssize_typeiterator _M_erase (size_type __bkt, __node_base *__prev_n, __node_type *__n)β erase node__nin bucket__bktwith previous node__prev_nsize_type _M_erase (true_type, const key_type &)β erase path withtrue_type, returnssize_type__node_base * _M_find_before_node (size_type, const key_type &, __hash_code) constβ finds the node before the one you're looking for, returns__node_base *__node_type * _M_find_node (size_type __bkt, const key_type &__key, __hash_code __c) constβ finds the node for__keyin bucket__bktwith hash__c__node_base * _M_get_previous_node (size_type __bkt, __node_base *__n)β grabs the previous node of__nin bucket__bkttemplate<typename _Arg , typename _NodeGenerator > std::pair< iterator, bool > _M_insert (_Arg &&, const _NodeGenerator &, true_type, size_type=1)β protected insert,true_type, defaultsize_type=1, returnsstd::pair< iterator, bool >template<typename _Arg , typename _NodeGenerator > iterator _M_insert (_Arg &&__arg, const _NodeGenerator &__node_gen, false_type __uk)β protected insert withfalse_type __uk, returnsiteratortemplate<typename _Arg , typename _NodeGenerator > iterator _M_insert (const_iterator, _Arg &&, const _NodeGenerator &, false_type)β protected hinted insert withfalse_typetemplate<typename _Arg , typename _NodeGenerator > iterator _M_insert (const_iterator, _Arg &&__arg, const _NodeGenerator &__node_gen, true_type __uk)β protected hinted insert withtrue_type __ukvoid _M_insert_bucket_begin (size_type, __node_type *)β inserts at the beginning of a bucketiterator _M_insert_multi_node (__node_type *__hint, const key_type &__k, __hash_code __code, __node_type *__n)β the multi-key node insert, with__hint, key__k, code__code, node__niterator _M_insert_unique_node (const key_type &__k, size_type __bkt, __hash_code __code, __node_type *__n, size_type __n_elt=1)β the unique-key node insert, with__n_elt=1by defaultvoid _M_remove_bucket_begin (size_type __bkt, __node_type *__next_n, size_type __next_bkt)β removes the beginning of bucket__bkt, next node__next_n, next bucket__next_bktvoid _M_swap (_Hashtable_base &__x)β swaps internals with_Hashtable_base &__x
π Friends π
Her inner circle β these structs get to see her private bits:
template<typename _Keya , typename _Valuea , typename _Alloca , typename _ExtractKeya , typename _Equala , typename _H1a , typename _H2a , typename _Hasha , typename _RehashPolicya , typename _Traitsa , bool _Unique_keysa> struct __detail::_Equalityβ friend struct__detail::_Equalitytemplate<typename _Keya , typename _Valuea , typename _Alloca , typename _ExtractKeya , typename _Equala , typename _H1a , typename _H2a , typename _Hasha , typename _RehashPolicya , typename _Traitsa , bool _Constant_iteratorsa> struct __detail::_Insertβ friend struct__detail::_Inserttemplate<typename _Keya , typename _Valuea , typename _Alloca , typename _ExtractKeya , typename _Equala , typename _H1a , typename _H2a , typename _Hasha , typename _RehashPolicya , typename _Traitsa > struct __detail::_Insert_baseβ friend struct__detail::_Insert_basetemplate<typename _Keya , typename _Valuea , typename _Alloca , typename _ExtractKeya , typename _Equala , typename _H1a , typename _H2a , typename _Hasha , typename _RehashPolicya , typename _Traitsa , bool _Unique_keysa> struct __detail::_Map_baseβ friend struct__detail::_Map_base
Part 1 done, bestie β but we are not done, part 2 is loadingβ¦ π β¨
β¨ Detailed Description β¨
okay bestie, buckle up, because this is the main character of the whole page π
template<typename _Key, typename _Value, typename _Alloc, typename
_ExtractKey, typename _Equal, typename _H1, typename _H2, typename
_Hash, typename _RehashPolicy, typename _Traits>
class std::_Hashtable< _Key, _Value, _Alloc, _ExtractKey, _Equal, _H1,
_H2, _Hash, _RehashPolicy, _Traits >
This is the Primary class template _Hashtable. Yes, the one. The blueprint behind the scenes. π
π§© Template Parameters
_Valueβ a CopyConstructible type. It has to be copyable, no exceptions, that's the vibe. π«Ά_Keyβ also a CopyConstructible type._Allocβ an allocator type ([lib.allocator.requirements]) whose_Alloc::value_typeis_Value. And here's the fun part: as a conforming extension, we allow_Alloc::value_type != _Value. Iconic of them tbh. β¨_ExtractKeyβ a function object that takes an object of type_Valueand returns a value of type_Key. It's the "so what's the key here" guy. π_Equalβ a function object that takes two objects of typekand returns a bool-like value that istrueif the two objects are considered equal. Lowkey just a fancy==with commitment. π_H1β the hash function. A unary function object with argument type_Keyand result typesize_t. Return values should be distributed over the entire range[0, numeric_limits<size_t>:::max()]. Spread it out, don't be shy. π_H2β the range-hashing function (in the terminology of Tavori and Dreizin). A binary function object whose argument types and result type are allsize_t. Given argumentsrandN, the return value is in the range[0, N). Noted, kings. π_Hashβ the ranged hash function (Tavori and Dreizin, again, they're famous here). A binary function whose argument types are_Keyandsize_tand whose result type issize_t. Given argumentskandN, the return value is in the range[0, N). Default:hash(k, N) = h2(h1(k), N). And hear me out β if_Hashis anything other than the default,_H1and_H2are ignored. Periodt. π_RehashPolicyβ a policy class with three members, and all three of them govern the bucket count. So:_M_next_bkt(n)returns a bucket count no smaller thann._M_bkt_for_elements(n)returns a bucket count appropriate for an element count ofn._M_need_rehash(n_bkt, n_elt, n_ins)decides whether, if the current bucket count isn_bktand the current element count isn_elt, we need to increase the bucket count. If so, it returnsmake_pair(true, n), wherenis the new bucket count. If not, it returnsmake_pair(false, <anything>). The<anything>is sending me ngl π
_Traitsβ a compile-time class with three booleanstd::integral_constantmembers:__cache_hash_code,__constant_iterators,__unique_keys. Three little booleans doing the most. π
ποΈ Every _Hashtable data structure has:
_Bucket[] _M_buckets_Hash_node_base _M_before_beginsize_type _M_bucket_countsize_type _M_element_count
with _Bucket being _Hash_node* and _Hash_node containing:
_Hash_node* _M_nextTp _M_valuesize_t _M_hash_codeifcache_hash_codeis true
π§Ί In terms of Standard containers, the hashtable is like the aggregation of:
std::forward_list<_Node>containing the elementsstd::vector<std::forward_list<_Node>::iterator>representing the buckets
πΈ The Actual Tea (implementation notes)
The non-empty buckets contain the node before the first node in the bucket. This design makes it possible to implement something like a std::forward_list::insert_after on container insertion and std::forward_list::erase_after on container erase calls. _M_before_begin is equivalent to std::forward_list::before_begin. Empty buckets contain nullptr.
π¨ okay bestie listen up: one of the non-empty buckets contains &_M_before_begin, which is not a dereferenceable node, so the node pointer in a bucket shall never be dereferenced β only its next node can be. Do not try it. I'm serious. π
Walking through a bucket's nodes requires a check on the hash code to see if each node is still in the bucket. Such a design assumes a quite efficient hash functor, and this is one of the reasons it is highly advisable to set __cache_hash_code to true. Do it. For me. π
The container iterators are simply built from nodes. This way incrementing the iterator is perfectly efficient independent of how many empty buckets there are in the container. Like, perfectly efficient. Slay. β
On insert we compute the element's hash code and use it to find the bucket index. If the element must be inserted in an empty bucket we add it at the beginning of the singly linked list and make the bucket point to _M_before_begin. The bucket that used to point to _M_before_begin, if any, is updated to point to its new before begin node. It's giving choreography. π¦
On erase, the simple iterator design requires using the hash functor to get the index of the bucket to update. For this reason, when __cache_hash_code is set to false the hash functor must not throw, and this is enforced by a static assertion. The compiler will catch you. It always does. π€
Functionality is implemented by decomposition into base classes, where the derived _Hashtable class is used in _Map_base, _Insert, _Rehash_base, and _Equality base classes to access the 'this' pointer. _Hashtable_base is used in the base classes as a non-recursive, fully-completed-type so that detailed nested type information, such as iterator type and node type, can be used. This is similar to the 'Curiously Recurring Template Pattern' (CRTP) technique, but uses a reconstructed, not explicitly passed, template pattern. So close to CRTP but not quite β it's giving "we have CRTP at home". π
𧬠Base class templates are:
__detail::_Hashtable_base__detail::_Map_base__detail::_Insert__detail::_Rehash_base__detail::_Equality
π Author
Generated automatically by Doxygen for libstdc++ from the source code.
and that's the whole page bestie, we ate and left no crumbs πβ¨ this lil masterpiece lives in the libstdc++ docs, was auto-generated by Doxygen straight from the source code, and it's filed under section 3cxx β the footer itself is just that mangled libstdstd::_Hashtabl...licy, _Traits >(3cxx) title treatment, so no date to give you, sorry, the manpage didn't leave a timestamp. mwah, go set __cache_hash_code to true ππ«Άπ
π¦ where this page comes from
- ubuntu noble: libstdc++-10-doc 10.5.0-4ubuntu2
- ubuntu noble: libstdc++-10-doc 10.5.0-4ubuntu2.1
- π girlified by deepseek-ai/DeepSeek-V4.1-Flash on : a language model's rewrite of the original, under its terms