I am trying to create an std::unordered_map with an std::pair as key. As you can imagine, this would require me to explicitly provide a class to generate a hash for a given key, as well as an equality comparator for the keys. Here's my code so far:
#include <unordered_map>
#include <memory>
#include <utility>
template <class T, typename U>
struct PairHash{
size_t operator()(const std::pair<T, U> &key){
return std::hash<T>()(key.first) ^ std::hash<U>()(key.second);
}
};
template <class T, typename U>
struct PairEqual{
bool operator()(const std::pair<T, U> &lhs, const std::pair<T, U> &rhs) const{
return lhs.first == rhs.first && lhs.second == rhs.second;
}
};
struct GraphEdge{
};
int main(){
std::unordered_map<std::pair<int, int>,
std::unique_ptr<GraphEdge>,
PairHash<int, int>,
PairEqual<int, int>> edges;
}
However, this gives me a rather (to my eyes at least) inscrutable compiler error:
In file included from /usr/include/c++/5/bits/hashtable.h:35:0,
from /usr/include/c++/5/unordered_map:47,
from prog.cpp:1:
/usr/include/c++/5/bits/hashtable_policy.h: In instantiation of 'struct std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> >':
/usr/include/c++/5/type_traits:137:12: required from 'struct std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > >'
/usr/include/c++/5/type_traits:148:38: required from 'struct std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
/usr/include/c++/5/bits/unordered_map.h:100:66: required from 'class std::unordered_map<std::pair<int, int>, std::unique_ptr<GraphEdge>, PairHash<int, int>, PairEqual<int, int> >'
prog.cpp:29:43: required from here
/usr/include/c++/5/bits/hashtable_policy.h:85:34: error: no match for call to '(const PairHash<int, int>) (const std::pair<int, int>&)'
noexcept(declval<const _Hash&>()(declval<const _Key&>()))>
^
prog.cpp:7:12: note: candidate: size_t PairHash<T, U>::operator()(const std::pair<_T1, _T2>&) [with T = int; U = int; size_t = unsigned int] <near match>
size_t operator()(const std::pair<T, U> &key){
^
prog.cpp:7:12: note: passing 'const PairHash<int, int>*' as 'this' argument discards qualifiers
In file included from /usr/include/c++/5/bits/move.h:57:0,
from /usr/include/c++/5/bits/stl_pair.h:59,
from /usr/include/c++/5/utility:70,
from /usr/include/c++/5/unordered_map:38,
from prog.cpp:1:
/usr/include/c++/5/type_traits: In instantiation of 'struct std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >':
/usr/include/c++/5/bits/unordered_map.h:100:66: required from 'class std::unordered_map<std::pair<int, int>, std::unique_ptr<GraphEdge>, PairHash<int, int>, PairEqual<int, int> >'
prog.cpp:29:43: required from here
/usr/include/c++/5/type_traits:148:38: error: 'value' is not a member of 'std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > >'
: public integral_constant<bool, !_Pp::value>
^
In file included from /usr/include/c++/5/unordered_map:48:0,
from prog.cpp:1:
/usr/include/c++/5/bits/unordered_map.h: In instantiation of 'class std::unordered_map<std::pair<int, int>, std::unique_ptr<GraphEdge>, PairHash<int, int>, PairEqual<int, int> >':
prog.cpp:29:43: required from here
/usr/include/c++/5/bits/unordered_map.h:100:66: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef __umap_hashtable<_Key, _Tp, _Hash, _Pred, _Alloc> _Hashtable;
^
/usr/include/c++/5/bits/unordered_map.h:107:45: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::key_type key_type;
^
/usr/include/c++/5/bits/unordered_map.h:108:47: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::value_type value_type;
^
/usr/include/c++/5/bits/unordered_map.h:109:48: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::mapped_type mapped_type;
^
/usr/include/c++/5/bits/unordered_map.h:110:43: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::hasher hasher;
^
/usr/include/c++/5/bits/unordered_map.h:111:46: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::key_equal key_equal;
^
/usr/include/c++/5/bits/unordered_map.h:112:51: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::allocator_type allocator_type;
^
/usr/include/c++/5/bits/unordered_map.h:117:45: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::pointer pointer;
^
/usr/include/c++/5/bits/unordered_map.h:118:50: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::const_pointer const_pointer;
^
/usr/include/c++/5/bits/unordered_map.h:119:47: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::reference reference;
^
/usr/include/c++/5/bits/unordered_map.h:120:52: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::const_reference const_reference;
^
/usr/include/c++/5/bits/unordered_map.h:121:46: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::iterator iterator;
^
/usr/include/c++/5/bits/unordered_map.h:122:51: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::const_iterator const_iterator;
^
/usr/include/c++/5/bits/unordered_map.h:123:51: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::local_iterator local_iterator;
^
/usr/include/c++/5/bits/unordered_map.h:124:57: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::const_local_iterator const_local_iterator;
^
/usr/include/c++/5/bits/unordered_map.h:125:47: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::size_type size_type;
^
/usr/include/c++/5/bits/unordered_map.h:126:52: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
typedef typename _Hashtable::difference_type difference_type;
^
/usr/include/c++/5/bits/unordered_map.h:280:7: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
operator=(initializer_list<value_type> __l)
^
/usr/include/c++/5/bits/unordered_map.h:379:2: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
emplace(_Args&&... __args)
^
/usr/include/c++/5/bits/unordered_map.h:432:7: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
insert(const value_type& __x)
^
/usr/include/c++/5/bits/unordered_map.h:439:2: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
insert(_Pair&& __x)
^
/usr/include/c++/5/bits/unordered_map.h:499:7: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
insert(initializer_list<value_type> __l)
^
/usr/include/c++/5/bits/unordered_map.h:645:7: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
equal_range(const key_type& __x)
^
/usr/include/c++/5/bits/unordered_map.h:649:7: error: 'value' is not a member of 'std::__not_<std::__and_<std::__is_fast_hash<PairHash<int, int> >, std::__detail::__is_noexcept_hash<std::pair<int, int>, PairHash<int, int> > > >'
equal_range(const key_type& __x) const
^
What am I doing wrong?
Unordered Map does not contain a hash function for a pair like it has for int, string, etc, So if we want to hash a pair then we have to explicitly provide it with a hash function that can hash a pair.
A C++ map is a way to store a key-value pair. A map can be declared as follows: #include <iostream> #include <map> map<int, int> sample_map; Each map entry consists of a pair: a key and a value.
An unordered set of pairs is an unordered set in which each element is a pair itself. By default, C++ doesn't allow us to create an unordered set of pairs directly but one can pass a hash function to the unordered set container.
However, std::pair is not hashable by default, so a simple snippet like the above would not work. There are many proposals online to define a pairhash class and explicitly specify it as the hash function as a template parameter to std::unordered_set and std::unordered_map . This is not a bad idea.
Apparently, libstdc++ refers to your hash object via const PairHash<int, int>*
. Thus calling the operator ()
which is not marked const
in your program is a compiler error.
You can get your code to compile with libstdc++ by making operator()
const
.
As of 17.6.3.4 (Hash Requirements), a Hash type must provide a size_t operator(KeyType) const;
, so your code is indeed incorrect.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With