Last updated:2026-10-12
STL unordered_map source code analysis¶
0.Introduction¶
We learned about hashtable earlier; this section covers its container adaptor: unordered_map.
So the unordered map's underlying container is a hashtable.
The source of unordered_map and unordered_multimap lives in the file unordered_map.h.
1.The essential difference between unordered_map and unordered_multimap¶
First, a look at the unordered_map source:
template<class _Key, class _Tp,
class _Hash = hash<_Key>,
class _Pred = std::equal_to<_Key>,
class _Alloc = std::allocator<std::pair<const _Key, _Tp> > >
class unordered_map
{
typedef __umap_hashtable<_Key, _Tp, _Hash, _Pred, _Alloc> _Hashtable;
_Hashtable _M_h;
};
Go look at the declaration of the underlying container __umap_hashtable:
template<bool _Cache>
using __umap_traits = __detail::_Hashtable_traits<_Cache, false, true>;
template<typename _Key,
typename _Tp,
typename _Hash = hash<_Key>,
typename _Pred = std::equal_to<_Key>,
typename _Alloc = std::allocator<std::pair<const _Key, _Tp> >,
typename _Tr = __umap_traits<__cache_default<_Key, _Hash>::value>>
using __umap_hashtable = _Hashtable<_Key, std::pair<const _Key, _Tp>,
_Alloc, __detail::_Select1st,
_Pred, _Hash,
__detail::_Mod_range_hashing,
__detail::_Default_ranged_hash,
__detail::_Prime_rehash_policy, _Tr>;
template<typename _Key, typename _Value, typename _Alloc,
typename _ExtractKey, typename _Equal,
typename _H1, typename _H2, typename _Hash,
typename _RehashPolicy, typename _Traits>
_Tr is as follows:
_Tr uses__umap_traits; let's keep going:
Comparing the two above, we find that inside __umap_traits there is a string of __cache_default. The template parameter is of bool type, so we can print whether it is false or true; by actual testing it is false, meaning the hash code is not cached.
template<typename _Tp, typename _Hash>
using __cache_default
= __not_<__and_<__is_fast_hash<_Hash>,
__detail::__is_noexcept_hash<_Tp, _Hash>>>;
__umap_traits, which actually calls _Hashtable_traits; we keep going:
template<bool _Cache_hash_code, bool _Constant_iterators, bool _Unique_keys>
struct _Hashtable_traits
{
using __hash_cached = __bool_constant<_Cache_hash_code>;
using __constant_iterators = __bool_constant<_Constant_iterators>;
using __unique_keys = __bool_constant<_Unique_keys>;
};
/// Base types for unordered_multimap.
template<bool _Cache>
using __ummap_traits = __detail::_Hashtable_traits<_Cache, false, false>;
To sum up, the analysis above shows us the essential difference between unordered_map and unordered_multimap, and also how a single underlying container implements two container adaptors in the source!
2.The essential difference between unordered_set and unordered_multiset¶
The analysis is the same as before; first look at unordered_set:
template<class _Value,
class _Hash = hash<_Value>,
class _Pred = std::equal_to<_Value>,
class _Alloc = std::allocator<_Value> >
class unordered_set
{
typedef __uset_hashtable<_Value, _Hash, _Pred, _Alloc> _Hashtable;
_Hashtable _M_h;
}
template<bool _Cache>
using __uset_traits = __detail::_Hashtable_traits<_Cache, true, true>;
template<typename _Value,
typename _Hash = hash<_Value>,
typename _Pred = std::equal_to<_Value>,
typename _Alloc = std::allocator<_Value>,
typename _Tr = __uset_traits<__cache_default<_Value, _Hash>::value>>
using __uset_hashtable = _Hashtable<_Value, _Value, _Alloc,
__detail::_Identity, _Pred, _Hash,
__detail::_Mod_range_hashing,
__detail::_Default_ranged_hash,
__detail::_Prime_rehash_policy, _Tr>;
_Hashtable_traits is false,true,true. For unordered_set, a const iterator and unique keys are used; now let's look at unordered_multiset:
Comparing the two again: essentially, unordered_set does not allow duplicate keys, while unordered_multiset allows duplicate keys.
3.Three conclusions¶
Now, with the previous groundwork, let's list the four container adaptors in turn:
(1) unordered_map
(2) unordered_multimap (3) unordered_set (4) unordered_multisetAfter comparing, we conclude:
- Conclusion 1: unordered_map and unordered_set do not allow duplicate keys, while the multi variants do allow duplicate keys;
- Conclusion 2: unordered_map and unordered_multimap use iterator as their iterator, while unordered_set and unordered_multiset use const_iterator.
- Conclusion 3: for unordered_map and unordered_multimap the key is the key and the value is key+value; while for unordered_set and unordered_multiset the key is the Value, and the Value is also the Key.
For the last conclusion, compare the following (look at the first and second parameters passed to the hashtable):
unordered_map and unordered_multimap:
using __umap_hashtable = _Hashtable<_Key,
std::pair<const _Key, _Tp>,
_Alloc, __detail::_Select1st,
_Pred, _Hash,
__detail::_Mod_range_hashing,
__detail::_Default_ranged_hash,
__detail::_Prime_rehash_policy, _Tr>;
template<typename _Value,
typename _Hash = hash<_Value>,
typename _Pred = std::equal_to<_Value>,
typename _Alloc = std::allocator<_Value>,
typename _Tr = __uset_traits<__cache_default<_Value, _Hash>::value>>
using __uset_hashtable = _Hashtable<_Value, _Value, _Alloc,
__detail::_Identity, _Pred, _Hash,
__detail::_Mod_range_hashing,
__detail::_Default_ranged_hash,
__detail::_Prime_rehash_policy, _Tr>;
4.Important functions of unordered_map¶
Initialization
In the constructor below you can see that unordered_map's default bucket count is 10.
At its base, unordered_map defaults to hasher(), which is H1, which is std::hash
unordered_map(size_type __n = 10,
const hasher& __hf = hasher(),
const key_equal& __eql = key_equal(),
const allocator_type& __a = allocator_type())
: _M_h(__n, __hf, __eql, __a)
{ }
unordered_map<string,int> um;
hash<string> h;
cout<<um.hash_function()("hhhhhawq")<<endl;
cout<<h("hhhhhawq")<<endl;
Empty, size, maximum size
bool
empty() const noexcept
{ return _M_h.empty(); }
/// Returns the size of the %unordered_map.
size_type
size() const noexcept
{ return _M_h.size(); }
/// Returns the maximum size of the %unordered_map.
size_type
max_size() const noexcept
{ return _M_h.max_size(); }
begin and end
insert Five ways to insert
// value
std::pair<iterator, bool>
insert(const value_type& __x)
{ return _M_h.insert(__x); }
// pair
std::pair<iterator, bool>
insert(_Pair&& __x)
{ return _M_h.insert(std::forward<_Pair>(__x)); }
// iterator+value
iterator
insert(const_iterator __hint, const value_type& __x)
{ return _M_h.insert(__hint, __x); }
// first到last范围插入
template<typename _InputIterator>
void
insert(_InputIterator __first, _InputIterator __last)
{ _M_h.insert(__first, __last); }
// 初始化列表插入
void
insert(initializer_list<value_type> __l)
{ _M_h.insert(__l); }
Erase
Three ways to erase
// iterator
iterator
erase(iterator __position)
{ return _M_h.erase(__position); }
// key
size_type
erase(const key_type& __x)
{ return _M_h.erase(__x); }
// first到last范围
iterator
erase(const_iterator __first, const_iterator __last)
{ return _M_h.erase(__first, __last);
Clear
hash_function
Obtain this unordered_map's hash_function
Usage:key_eq key_eq returns std::equal_to Used as follows:
Find and get values
iterator
find(const key_type& __x)
{ return _M_h.find(__x); }
mapped_type&
operator[](const key_type& __k)
{ return _M_h[__k]; }
mapped_type&
at(const key_type& __k)
{ return _M_h.at(__k); }
Similarly, unordered_set, unordered_multiset, and unordered_multimap have the same functions as unordered_map, so they need no elaboration.
Related Topics¶
- STL array source code analysis | C++那些事
- STL string deep dive | C++那些事
- STL string interview questions in depth | C++那些事
- STL deque source code analysis | C++那些事
- STL empty base optimization EBO | C++那些事
- STL hand-written hash table implementation | C++那些事
- STL hashtable hash table | C++那些事
- STL iterator source code analysis | C++那些事
- STL list source code analysis | C++那些事
- STL map and multimap | C++那些事