Map "int Triplets" to int? - c++

using c++ std's unordered_map i want to map an integer triplet to a single integer, i usually don't use hash tables(didn't know they were so cool), but i don't know the right approach in this case, using the default hashing function should i map the triplets directly (something like < < int,int >,int >->int)
std::unordered_map <std::make_pair <make_pair <int,int>,int>,int> hash;
or maybe use a function to map the triplet to a single value and the use that value with the default function?
int mapping(int a, int b, int c){
}
std::unordered_map <int,int> hash;
both approaches work but i'd like to know wich one is the most efficient one. thank you

First off, you would use std::tuple<int, int, int> as the key type.
Next, you need a way to hash a tuple given that you can hash each element. There is a function called hash_combine in Boost that does that, but for reasons unclear to me, that one was not included in the standard. Anyway, here it goes:
#include <tuple>
#include <utility>
template <class T>
inline void hash_combine(std::size_t & seed, const T & v)
{
std::hash<T> hasher;
seed ^= hasher(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
template <class Tuple, std::size_t Index = std::tuple_size<Tuple>::value - 1>
struct tuple_hash_impl
{
static inline void apply(std::size_t & seed, Tuple const & tuple)
{
tuple_hash_impl<Tuple, Index - 1>::apply(seed, tuple);
hash_combine(seed, std::get<Index>(tuple));
}
};
template <class Tuple>
struct tuple_hash_impl<Tuple, 0>
{
static inline void apply(std::size_t & seed, Tuple const & tuple)
{
hash_combine(seed, std::get<0>(tuple));
}
};
namespace std
{
template<typename S, typename T> struct hash<pair<S, T>>
{
inline size_t operator()(const pair<S, T> & v) const
{
size_t seed = 0;
::hash_combine(seed, v.first);
::hash_combine(seed, v.second);
return seed;
}
};
template<typename ...Args> struct hash<tuple<Args...>>
{
inline size_t operator()(const tuple<Args...> & v) const
{
size_t seed = 0;
tuple_hash_impl<tuple<Args...>>::apply(seed, v);
return seed;
}
};
}

"Most efficient" seems like something depending on your compiler, but I would say that the make_pair solution looks like a mess. Better use your own hash function... just make sure you make up a decent one :)

Your solution with a pair of pairs should be pretty efficient. It will be difficult to map three integers to something simpler as far as hashing is concerned.

Related

Making a static map with pair as a key [duplicate]

I am trying to create an unordered_map to map pairs with integers:
#include <unordered_map>
using namespace std;
using Vote = pair<string, string>;
using Unordered_map = unordered_map<Vote, int>;
I have a class where I have declared an Unordered_map as a private member.
However, I am getting the following error when I try to compile it:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/type_traits:948:38: Implicit instantiation of undefined template 'std::__1::hash, std::__1::basic_string > >'
I am not getting this error if I use a regular map like map<pair<string, string>, int> instead of an unordered_map.
Is it not possible to use pair as key in unordered maps?
You need to provide a suitable hash function for your key type. A simple example:
#include <unordered_map>
#include <functional>
#include <string>
#include <utility>
// Only for pairs of std::hash-able types for simplicity.
// You can of course template this struct to allow other hash functions
struct pair_hash {
template <class T1, class T2>
std::size_t operator () (const std::pair<T1,T2> &p) const {
auto h1 = std::hash<T1>{}(p.first);
auto h2 = std::hash<T2>{}(p.second);
// Mainly for demonstration purposes, i.e. works but is overly simple
// In the real world, use sth. like boost.hash_combine
return h1 ^ h2;
}
};
using Vote = std::pair<std::string, std::string>;
using Unordered_map = std::unordered_map<Vote, int, pair_hash>;
int main() {
Unordered_map um;
}
This will work, but not have the best hash-properties†. You might want to have a look at something like boost.hash_combine for higher quality results when combining the hashes. This is also discussed in greater detail – including the aforementioned solution from boost – in this answer.
For real world use: Boost also provides the function set hash_value which already provides a hash function for std::pair, as well as std::tuple and most standard containers.
†More precisely, it will produce too many collisions. E.g., every symmetric pair will hash to 0 and pairs that differ only by permutation will have the same hash. This is probably fine for your programming exercise, but might seriously hurt performance of real world code.
My preferred way of solving this problem is to define a key function that transforms your pair into a unique integer (or any hashable data type). This key is not the hash key. It is the unique ID of the pair of data that will then be optimally hashed by the unordered_map. For example, you wanted to define an unordered_map of the type
unordered_map<pair<int,int>,double> Map;
And you want to use Map[make_pair(i,j)]=value or Map.find(make_pair(i,j)) to operate on the map. Then you'll have to tell the system how to hash a pair of integers make_pair(i,j). Instead of that, we can define
inline size_t key(int i,int j) {return (size_t) i << 32 | (unsigned int) j;}
and then change the type of the map to
unordered_map<size_t,double> Map;
We can now use Map[key(i,j)]=value or Map.find(key(i,j)) to operate on the map. Every make_pair now becomes calling the inline key function.
This method guarantees that the key will be optimally hashed, because now the hashing part is done by the system, which will always choose the internal hash table size to be prime to make sure every bucket is equally likely. But you have to make yourself 100% sure that the key is unique for every pair, i.e., no two distinct pairs can have the same key, or there can be very difficult bugs to find.
If using pair is not a strict requirement, you can simply use map twice.
#include <unordered_map>
using namespace std;
using Unordered_map = unordered_map<string, unordered_map<string, int>>;
Unordered_map um;
um["Region1"]["Candidate1"] = 10;
cout << um["Region1"]["Candidate1"]; // 10
For pair key, we can use boost pair hash function:
#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
using namespace std;
int main() {
unordered_map<pair<string, string>, int, boost::hash<pair<string, string>>> m;
m[make_pair("123", "456")] = 1;
cout << m[make_pair("123", "456")] << endl;
return 0;
}
Similarly we can use boost hash for vectors,
#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
#include <vector>
using namespace std;
int main() {
unordered_map<vector<string>, int, boost::hash<vector<string>>> m;
vector<string> a({"123", "456"});
m[a] = 1;
cout << m[a] << endl;
return 0;
}
Reference: C++ Standard Library: A tutorial and reference, Second version Chapter 7.9.2: Creating and Controlling unordered Container
All solutions I found in Google use XOR to generate hashcode of pair, which is totally bad. see why-is-xor-the-default-way-to-combine-hashes. However, the book has given us the best solution, using hash_combine, which is taken from Boost. The solution is much better than XOR when I tested it in Online Judge(Atcoder). I organized the code as a template as follow. You can copy and paste it as much as you can. And it is convenient to change it to fit any custom struct/class.
Update: add hash template for tuple.
#include <functional>
namespace hash_tuple {
template <typename TT> struct hash {
size_t operator()(TT const &tt) const { return std::hash<TT>()(tt); }
};
// from boost (functional/hash):
// see http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html template
template <class T> inline void hash_combine(std::size_t &seed, T const &v) {
seed ^= hash_tuple::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
// Recursive template code derived from Matthieu M.
template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>
struct HashValueImpl {
void operator()(size_t &seed, Tuple const &tuple) const {
HashValueImpl<Tuple, Index - 1>{}(seed, tuple);
hash_combine(seed, std::get<Index>(tuple));
}
};
template <class Tuple> struct HashValueImpl<Tuple, 0> {
void operator()(size_t &seed, Tuple const &tuple) const {
hash_combine(seed, std::get<0>(tuple));
}
};
template <typename... TT> struct hash<std::tuple<TT...>> {
size_t operator()(std::tuple<TT...> const &tt) const {
size_t seed = 0;
HashValueImpl<std::tuple<TT...>>{}(seed, tt);
return seed;
}
};
// auxiliary generic functions to create a hash value using a seed
template <typename T> inline void hash_val(std::size_t &seed, const T &val) {
hash_combine(seed, val);
}
template <typename T, typename... Types>
inline void hash_val(std::size_t &seed, const T &val, const Types &... args) {
hash_combine(seed, val);
hash_val(seed, args...);
}
template <typename... Types>
inline std::size_t hash_val(const Types &... args) {
std::size_t seed = 0;
hash_val(seed, args...);
return seed;
}
struct pair_hash {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2> &p) const {
return hash_val(p.first, p.second);
}
};
} // namespace hash_tuple
#include <bits/stdc++.h>
int main() {
using ll = long long;
// std::unordered_map<std::pair<ll, ll>, ll, hash_tuple::pair_hash>
// hashmapPair; std::unordered_set<std::pair<ll, ll>, hash_tuple::pair_hash>
// hashsetPair;
std::unordered_map<std::pair<ll, ll>, ll, hash_tuple::pair_hash>
hashmapPair;
hashmapPair[{0, 0}] = 10;
std::unordered_set<std::pair<ll, ll>, hash_tuple::pair_hash> hashsetPair;
hashsetPair.insert({1, 1});
using TI = std::tuple<ll, ll, ll, ll>;
std::unordered_map<TI, ll, hash_tuple::hash<TI>> hashmapTuple;
hashmapTuple[{0, 1, 2, 3}] = 10;
std::unordered_set<TI, hash_tuple::hash<TI>> hashsetTuple;
hashsetTuple.emplace(0, 1, 2, 3);
return 0;
}
As your compilation error indicates, there is no valid instantiation of std::hash<std::pair<std::string, std::string>> in your std namespace.
According to my compiler:
Error C2338 The C++ Standard doesn't provide a hash for this
type. c:\program files (x86)\microsoft visual studio
14.0\vc\include\xstddef 381
You can provide your own specialization for std::hash<Vote> as follows:
#include <string>
#include <unordered_map>
#include <functional>
using namespace std;
using Vote = pair<string, string>;
using Unordered_map = unordered_map<Vote, int>;
namespace std
{
template<>
struct hash<Vote>
{
size_t operator()(Vote const& v) const
{
// ... hash function here ...
}
};
}
int main()
{
Unordered_map m;
}
In the comments on the answer by Baum mit Augen, the user Joe Black asked for an example on using a lambda expressions instead of defining a hash function. I agree with the opinion of Baum mit Augen, that this might harm readability, especially if you want to implement a more universal solution. Therefore, I'd like to keep my example short by focusing on a specific solution for std::pair<std::string, std::string>, as presented by the OP. The example also uses a handcrafted combination of std::hash<std::string> function calls:
using Vote = std::pair<std::string, std::string>;
auto hash = [](const Vote& v){
return std::hash<std::string>()(v.first) * 31 + std::hash<std::string>()(v.second);
};
using Unordered_map = std::unordered_map<Vote, int, decltype(hash)>;
Unordered_map um(8, hash);
Code on Ideone
I've simplified #YoungForest's answer to only work with pairs (= not with arbitrary length tuples) as was requested by the OP. I've also minimized the boilerplate code:
#include <functional>
#include <iostream>
#include <unordered_map>
#include <utility> # pair
using namespace std;
// from boost (functional/hash):
// see http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html template
template <class T> inline void hash_combine(size_t &seed, T const &v) {
seed ^= hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
struct pair_hash {
template <class T1, class T2>
size_t operator()(const pair<T1, T2> &p) const {
size_t seed = 0;
hash_combine(seed, p.first);
hash_combine(seed, p.second);
return seed;
}
};
int main() {
unordered_map<pair<int, int>, int, pair_hash> d;
d[{1, 2}] = 3;
cout << d.find({1, 2})->second << endl;
return 0;
}
It uses the same logic as in the boost library (that is better than the xor version).
There is a hack to such problems
Use a std:unordered_map of string
Look at the following example-
I am required to hash the endpoint(corner) of a rectangle
Error Approach
unordered_map<pair<int, int>, int> M; //ERROR
pair<int, int> p;
M[p]++;
Hack
unordered_map<string, int> M;
pair<int, int> p;
string s = to_string(p.first) + "_" + to_string(p.second);
M[s]++;
Such hack even works if you are required to create a hash of decimal or double as a key :)

call to implicitly-deleted default constructor for design underground system [duplicate]

I am trying to create an unordered_map to map pairs with integers:
#include <unordered_map>
using namespace std;
using Vote = pair<string, string>;
using Unordered_map = unordered_map<Vote, int>;
I have a class where I have declared an Unordered_map as a private member.
However, I am getting the following error when I try to compile it:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/type_traits:948:38: Implicit instantiation of undefined template 'std::__1::hash, std::__1::basic_string > >'
I am not getting this error if I use a regular map like map<pair<string, string>, int> instead of an unordered_map.
Is it not possible to use pair as key in unordered maps?
You need to provide a suitable hash function for your key type. A simple example:
#include <unordered_map>
#include <functional>
#include <string>
#include <utility>
// Only for pairs of std::hash-able types for simplicity.
// You can of course template this struct to allow other hash functions
struct pair_hash {
template <class T1, class T2>
std::size_t operator () (const std::pair<T1,T2> &p) const {
auto h1 = std::hash<T1>{}(p.first);
auto h2 = std::hash<T2>{}(p.second);
// Mainly for demonstration purposes, i.e. works but is overly simple
// In the real world, use sth. like boost.hash_combine
return h1 ^ h2;
}
};
using Vote = std::pair<std::string, std::string>;
using Unordered_map = std::unordered_map<Vote, int, pair_hash>;
int main() {
Unordered_map um;
}
This will work, but not have the best hash-properties†. You might want to have a look at something like boost.hash_combine for higher quality results when combining the hashes. This is also discussed in greater detail – including the aforementioned solution from boost – in this answer.
For real world use: Boost also provides the function set hash_value which already provides a hash function for std::pair, as well as std::tuple and most standard containers.
†More precisely, it will produce too many collisions. E.g., every symmetric pair will hash to 0 and pairs that differ only by permutation will have the same hash. This is probably fine for your programming exercise, but might seriously hurt performance of real world code.
My preferred way of solving this problem is to define a key function that transforms your pair into a unique integer (or any hashable data type). This key is not the hash key. It is the unique ID of the pair of data that will then be optimally hashed by the unordered_map. For example, you wanted to define an unordered_map of the type
unordered_map<pair<int,int>,double> Map;
And you want to use Map[make_pair(i,j)]=value or Map.find(make_pair(i,j)) to operate on the map. Then you'll have to tell the system how to hash a pair of integers make_pair(i,j). Instead of that, we can define
inline size_t key(int i,int j) {return (size_t) i << 32 | (unsigned int) j;}
and then change the type of the map to
unordered_map<size_t,double> Map;
We can now use Map[key(i,j)]=value or Map.find(key(i,j)) to operate on the map. Every make_pair now becomes calling the inline key function.
This method guarantees that the key will be optimally hashed, because now the hashing part is done by the system, which will always choose the internal hash table size to be prime to make sure every bucket is equally likely. But you have to make yourself 100% sure that the key is unique for every pair, i.e., no two distinct pairs can have the same key, or there can be very difficult bugs to find.
If using pair is not a strict requirement, you can simply use map twice.
#include <unordered_map>
using namespace std;
using Unordered_map = unordered_map<string, unordered_map<string, int>>;
Unordered_map um;
um["Region1"]["Candidate1"] = 10;
cout << um["Region1"]["Candidate1"]; // 10
For pair key, we can use boost pair hash function:
#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
using namespace std;
int main() {
unordered_map<pair<string, string>, int, boost::hash<pair<string, string>>> m;
m[make_pair("123", "456")] = 1;
cout << m[make_pair("123", "456")] << endl;
return 0;
}
Similarly we can use boost hash for vectors,
#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
#include <vector>
using namespace std;
int main() {
unordered_map<vector<string>, int, boost::hash<vector<string>>> m;
vector<string> a({"123", "456"});
m[a] = 1;
cout << m[a] << endl;
return 0;
}
Reference: C++ Standard Library: A tutorial and reference, Second version Chapter 7.9.2: Creating and Controlling unordered Container
All solutions I found in Google use XOR to generate hashcode of pair, which is totally bad. see why-is-xor-the-default-way-to-combine-hashes. However, the book has given us the best solution, using hash_combine, which is taken from Boost. The solution is much better than XOR when I tested it in Online Judge(Atcoder). I organized the code as a template as follow. You can copy and paste it as much as you can. And it is convenient to change it to fit any custom struct/class.
Update: add hash template for tuple.
#include <functional>
namespace hash_tuple {
template <typename TT> struct hash {
size_t operator()(TT const &tt) const { return std::hash<TT>()(tt); }
};
// from boost (functional/hash):
// see http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html template
template <class T> inline void hash_combine(std::size_t &seed, T const &v) {
seed ^= hash_tuple::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
// Recursive template code derived from Matthieu M.
template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>
struct HashValueImpl {
void operator()(size_t &seed, Tuple const &tuple) const {
HashValueImpl<Tuple, Index - 1>{}(seed, tuple);
hash_combine(seed, std::get<Index>(tuple));
}
};
template <class Tuple> struct HashValueImpl<Tuple, 0> {
void operator()(size_t &seed, Tuple const &tuple) const {
hash_combine(seed, std::get<0>(tuple));
}
};
template <typename... TT> struct hash<std::tuple<TT...>> {
size_t operator()(std::tuple<TT...> const &tt) const {
size_t seed = 0;
HashValueImpl<std::tuple<TT...>>{}(seed, tt);
return seed;
}
};
// auxiliary generic functions to create a hash value using a seed
template <typename T> inline void hash_val(std::size_t &seed, const T &val) {
hash_combine(seed, val);
}
template <typename T, typename... Types>
inline void hash_val(std::size_t &seed, const T &val, const Types &... args) {
hash_combine(seed, val);
hash_val(seed, args...);
}
template <typename... Types>
inline std::size_t hash_val(const Types &... args) {
std::size_t seed = 0;
hash_val(seed, args...);
return seed;
}
struct pair_hash {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2> &p) const {
return hash_val(p.first, p.second);
}
};
} // namespace hash_tuple
#include <bits/stdc++.h>
int main() {
using ll = long long;
// std::unordered_map<std::pair<ll, ll>, ll, hash_tuple::pair_hash>
// hashmapPair; std::unordered_set<std::pair<ll, ll>, hash_tuple::pair_hash>
// hashsetPair;
std::unordered_map<std::pair<ll, ll>, ll, hash_tuple::pair_hash>
hashmapPair;
hashmapPair[{0, 0}] = 10;
std::unordered_set<std::pair<ll, ll>, hash_tuple::pair_hash> hashsetPair;
hashsetPair.insert({1, 1});
using TI = std::tuple<ll, ll, ll, ll>;
std::unordered_map<TI, ll, hash_tuple::hash<TI>> hashmapTuple;
hashmapTuple[{0, 1, 2, 3}] = 10;
std::unordered_set<TI, hash_tuple::hash<TI>> hashsetTuple;
hashsetTuple.emplace(0, 1, 2, 3);
return 0;
}
As your compilation error indicates, there is no valid instantiation of std::hash<std::pair<std::string, std::string>> in your std namespace.
According to my compiler:
Error C2338 The C++ Standard doesn't provide a hash for this
type. c:\program files (x86)\microsoft visual studio
14.0\vc\include\xstddef 381
You can provide your own specialization for std::hash<Vote> as follows:
#include <string>
#include <unordered_map>
#include <functional>
using namespace std;
using Vote = pair<string, string>;
using Unordered_map = unordered_map<Vote, int>;
namespace std
{
template<>
struct hash<Vote>
{
size_t operator()(Vote const& v) const
{
// ... hash function here ...
}
};
}
int main()
{
Unordered_map m;
}
In the comments on the answer by Baum mit Augen, the user Joe Black asked for an example on using a lambda expressions instead of defining a hash function. I agree with the opinion of Baum mit Augen, that this might harm readability, especially if you want to implement a more universal solution. Therefore, I'd like to keep my example short by focusing on a specific solution for std::pair<std::string, std::string>, as presented by the OP. The example also uses a handcrafted combination of std::hash<std::string> function calls:
using Vote = std::pair<std::string, std::string>;
auto hash = [](const Vote& v){
return std::hash<std::string>()(v.first) * 31 + std::hash<std::string>()(v.second);
};
using Unordered_map = std::unordered_map<Vote, int, decltype(hash)>;
Unordered_map um(8, hash);
Code on Ideone
I've simplified #YoungForest's answer to only work with pairs (= not with arbitrary length tuples) as was requested by the OP. I've also minimized the boilerplate code:
#include <functional>
#include <iostream>
#include <unordered_map>
#include <utility> # pair
using namespace std;
// from boost (functional/hash):
// see http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html template
template <class T> inline void hash_combine(size_t &seed, T const &v) {
seed ^= hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
struct pair_hash {
template <class T1, class T2>
size_t operator()(const pair<T1, T2> &p) const {
size_t seed = 0;
hash_combine(seed, p.first);
hash_combine(seed, p.second);
return seed;
}
};
int main() {
unordered_map<pair<int, int>, int, pair_hash> d;
d[{1, 2}] = 3;
cout << d.find({1, 2})->second << endl;
return 0;
}
It uses the same logic as in the boost library (that is better than the xor version).
There is a hack to such problems
Use a std:unordered_map of string
Look at the following example-
I am required to hash the endpoint(corner) of a rectangle
Error Approach
unordered_map<pair<int, int>, int> M; //ERROR
pair<int, int> p;
M[p]++;
Hack
unordered_map<string, int> M;
pair<int, int> p;
string s = to_string(p.first) + "_" + to_string(p.second);
M[s]++;
Such hack even works if you are required to create a hash of decimal or double as a key :)

unordered_map with a set<T> as key [duplicate]

I am trying to create an unordered_map to map pairs with integers:
#include <unordered_map>
using namespace std;
using Vote = pair<string, string>;
using Unordered_map = unordered_map<Vote, int>;
I have a class where I have declared an Unordered_map as a private member.
However, I am getting the following error when I try to compile it:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/type_traits:948:38: Implicit instantiation of undefined template 'std::__1::hash, std::__1::basic_string > >'
I am not getting this error if I use a regular map like map<pair<string, string>, int> instead of an unordered_map.
Is it not possible to use pair as key in unordered maps?
You need to provide a suitable hash function for your key type. A simple example:
#include <unordered_map>
#include <functional>
#include <string>
#include <utility>
// Only for pairs of std::hash-able types for simplicity.
// You can of course template this struct to allow other hash functions
struct pair_hash {
template <class T1, class T2>
std::size_t operator () (const std::pair<T1,T2> &p) const {
auto h1 = std::hash<T1>{}(p.first);
auto h2 = std::hash<T2>{}(p.second);
// Mainly for demonstration purposes, i.e. works but is overly simple
// In the real world, use sth. like boost.hash_combine
return h1 ^ h2;
}
};
using Vote = std::pair<std::string, std::string>;
using Unordered_map = std::unordered_map<Vote, int, pair_hash>;
int main() {
Unordered_map um;
}
This will work, but not have the best hash-properties†. You might want to have a look at something like boost.hash_combine for higher quality results when combining the hashes. This is also discussed in greater detail – including the aforementioned solution from boost – in this answer.
For real world use: Boost also provides the function set hash_value which already provides a hash function for std::pair, as well as std::tuple and most standard containers.
†More precisely, it will produce too many collisions. E.g., every symmetric pair will hash to 0 and pairs that differ only by permutation will have the same hash. This is probably fine for your programming exercise, but might seriously hurt performance of real world code.
My preferred way of solving this problem is to define a key function that transforms your pair into a unique integer (or any hashable data type). This key is not the hash key. It is the unique ID of the pair of data that will then be optimally hashed by the unordered_map. For example, you wanted to define an unordered_map of the type
unordered_map<pair<int,int>,double> Map;
And you want to use Map[make_pair(i,j)]=value or Map.find(make_pair(i,j)) to operate on the map. Then you'll have to tell the system how to hash a pair of integers make_pair(i,j). Instead of that, we can define
inline size_t key(int i,int j) {return (size_t) i << 32 | (unsigned int) j;}
and then change the type of the map to
unordered_map<size_t,double> Map;
We can now use Map[key(i,j)]=value or Map.find(key(i,j)) to operate on the map. Every make_pair now becomes calling the inline key function.
This method guarantees that the key will be optimally hashed, because now the hashing part is done by the system, which will always choose the internal hash table size to be prime to make sure every bucket is equally likely. But you have to make yourself 100% sure that the key is unique for every pair, i.e., no two distinct pairs can have the same key, or there can be very difficult bugs to find.
If using pair is not a strict requirement, you can simply use map twice.
#include <unordered_map>
using namespace std;
using Unordered_map = unordered_map<string, unordered_map<string, int>>;
Unordered_map um;
um["Region1"]["Candidate1"] = 10;
cout << um["Region1"]["Candidate1"]; // 10
For pair key, we can use boost pair hash function:
#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
using namespace std;
int main() {
unordered_map<pair<string, string>, int, boost::hash<pair<string, string>>> m;
m[make_pair("123", "456")] = 1;
cout << m[make_pair("123", "456")] << endl;
return 0;
}
Similarly we can use boost hash for vectors,
#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
#include <vector>
using namespace std;
int main() {
unordered_map<vector<string>, int, boost::hash<vector<string>>> m;
vector<string> a({"123", "456"});
m[a] = 1;
cout << m[a] << endl;
return 0;
}
Reference: C++ Standard Library: A tutorial and reference, Second version Chapter 7.9.2: Creating and Controlling unordered Container
All solutions I found in Google use XOR to generate hashcode of pair, which is totally bad. see why-is-xor-the-default-way-to-combine-hashes. However, the book has given us the best solution, using hash_combine, which is taken from Boost. The solution is much better than XOR when I tested it in Online Judge(Atcoder). I organized the code as a template as follow. You can copy and paste it as much as you can. And it is convenient to change it to fit any custom struct/class.
Update: add hash template for tuple.
#include <functional>
namespace hash_tuple {
template <typename TT> struct hash {
size_t operator()(TT const &tt) const { return std::hash<TT>()(tt); }
};
// from boost (functional/hash):
// see http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html template
template <class T> inline void hash_combine(std::size_t &seed, T const &v) {
seed ^= hash_tuple::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
// Recursive template code derived from Matthieu M.
template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>
struct HashValueImpl {
void operator()(size_t &seed, Tuple const &tuple) const {
HashValueImpl<Tuple, Index - 1>{}(seed, tuple);
hash_combine(seed, std::get<Index>(tuple));
}
};
template <class Tuple> struct HashValueImpl<Tuple, 0> {
void operator()(size_t &seed, Tuple const &tuple) const {
hash_combine(seed, std::get<0>(tuple));
}
};
template <typename... TT> struct hash<std::tuple<TT...>> {
size_t operator()(std::tuple<TT...> const &tt) const {
size_t seed = 0;
HashValueImpl<std::tuple<TT...>>{}(seed, tt);
return seed;
}
};
// auxiliary generic functions to create a hash value using a seed
template <typename T> inline void hash_val(std::size_t &seed, const T &val) {
hash_combine(seed, val);
}
template <typename T, typename... Types>
inline void hash_val(std::size_t &seed, const T &val, const Types &... args) {
hash_combine(seed, val);
hash_val(seed, args...);
}
template <typename... Types>
inline std::size_t hash_val(const Types &... args) {
std::size_t seed = 0;
hash_val(seed, args...);
return seed;
}
struct pair_hash {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2> &p) const {
return hash_val(p.first, p.second);
}
};
} // namespace hash_tuple
#include <bits/stdc++.h>
int main() {
using ll = long long;
// std::unordered_map<std::pair<ll, ll>, ll, hash_tuple::pair_hash>
// hashmapPair; std::unordered_set<std::pair<ll, ll>, hash_tuple::pair_hash>
// hashsetPair;
std::unordered_map<std::pair<ll, ll>, ll, hash_tuple::pair_hash>
hashmapPair;
hashmapPair[{0, 0}] = 10;
std::unordered_set<std::pair<ll, ll>, hash_tuple::pair_hash> hashsetPair;
hashsetPair.insert({1, 1});
using TI = std::tuple<ll, ll, ll, ll>;
std::unordered_map<TI, ll, hash_tuple::hash<TI>> hashmapTuple;
hashmapTuple[{0, 1, 2, 3}] = 10;
std::unordered_set<TI, hash_tuple::hash<TI>> hashsetTuple;
hashsetTuple.emplace(0, 1, 2, 3);
return 0;
}
As your compilation error indicates, there is no valid instantiation of std::hash<std::pair<std::string, std::string>> in your std namespace.
According to my compiler:
Error C2338 The C++ Standard doesn't provide a hash for this
type. c:\program files (x86)\microsoft visual studio
14.0\vc\include\xstddef 381
You can provide your own specialization for std::hash<Vote> as follows:
#include <string>
#include <unordered_map>
#include <functional>
using namespace std;
using Vote = pair<string, string>;
using Unordered_map = unordered_map<Vote, int>;
namespace std
{
template<>
struct hash<Vote>
{
size_t operator()(Vote const& v) const
{
// ... hash function here ...
}
};
}
int main()
{
Unordered_map m;
}
In the comments on the answer by Baum mit Augen, the user Joe Black asked for an example on using a lambda expressions instead of defining a hash function. I agree with the opinion of Baum mit Augen, that this might harm readability, especially if you want to implement a more universal solution. Therefore, I'd like to keep my example short by focusing on a specific solution for std::pair<std::string, std::string>, as presented by the OP. The example also uses a handcrafted combination of std::hash<std::string> function calls:
using Vote = std::pair<std::string, std::string>;
auto hash = [](const Vote& v){
return std::hash<std::string>()(v.first) * 31 + std::hash<std::string>()(v.second);
};
using Unordered_map = std::unordered_map<Vote, int, decltype(hash)>;
Unordered_map um(8, hash);
Code on Ideone
I've simplified #YoungForest's answer to only work with pairs (= not with arbitrary length tuples) as was requested by the OP. I've also minimized the boilerplate code:
#include <functional>
#include <iostream>
#include <unordered_map>
#include <utility> # pair
using namespace std;
// from boost (functional/hash):
// see http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html template
template <class T> inline void hash_combine(size_t &seed, T const &v) {
seed ^= hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
struct pair_hash {
template <class T1, class T2>
size_t operator()(const pair<T1, T2> &p) const {
size_t seed = 0;
hash_combine(seed, p.first);
hash_combine(seed, p.second);
return seed;
}
};
int main() {
unordered_map<pair<int, int>, int, pair_hash> d;
d[{1, 2}] = 3;
cout << d.find({1, 2})->second << endl;
return 0;
}
It uses the same logic as in the boost library (that is better than the xor version).
There is a hack to such problems
Use a std:unordered_map of string
Look at the following example-
I am required to hash the endpoint(corner) of a rectangle
Error Approach
unordered_map<pair<int, int>, int> M; //ERROR
pair<int, int> p;
M[p]++;
Hack
unordered_map<string, int> M;
pair<int, int> p;
string s = to_string(p.first) + "_" + to_string(p.second);
M[s]++;
Such hack even works if you are required to create a hash of decimal or double as a key :)

Trouble with unordered_map pair index [duplicate]

I am trying to create an unordered_map to map pairs with integers:
#include <unordered_map>
using namespace std;
using Vote = pair<string, string>;
using Unordered_map = unordered_map<Vote, int>;
I have a class where I have declared an Unordered_map as a private member.
However, I am getting the following error when I try to compile it:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/type_traits:948:38: Implicit instantiation of undefined template 'std::__1::hash, std::__1::basic_string > >'
I am not getting this error if I use a regular map like map<pair<string, string>, int> instead of an unordered_map.
Is it not possible to use pair as key in unordered maps?
You need to provide a suitable hash function for your key type. A simple example:
#include <unordered_map>
#include <functional>
#include <string>
#include <utility>
// Only for pairs of std::hash-able types for simplicity.
// You can of course template this struct to allow other hash functions
struct pair_hash {
template <class T1, class T2>
std::size_t operator () (const std::pair<T1,T2> &p) const {
auto h1 = std::hash<T1>{}(p.first);
auto h2 = std::hash<T2>{}(p.second);
// Mainly for demonstration purposes, i.e. works but is overly simple
// In the real world, use sth. like boost.hash_combine
return h1 ^ h2;
}
};
using Vote = std::pair<std::string, std::string>;
using Unordered_map = std::unordered_map<Vote, int, pair_hash>;
int main() {
Unordered_map um;
}
This will work, but not have the best hash-properties†. You might want to have a look at something like boost.hash_combine for higher quality results when combining the hashes. This is also discussed in greater detail – including the aforementioned solution from boost – in this answer.
For real world use: Boost also provides the function set hash_value which already provides a hash function for std::pair, as well as std::tuple and most standard containers.
†More precisely, it will produce too many collisions. E.g., every symmetric pair will hash to 0 and pairs that differ only by permutation will have the same hash. This is probably fine for your programming exercise, but might seriously hurt performance of real world code.
My preferred way of solving this problem is to define a key function that transforms your pair into a unique integer (or any hashable data type). This key is not the hash key. It is the unique ID of the pair of data that will then be optimally hashed by the unordered_map. For example, you wanted to define an unordered_map of the type
unordered_map<pair<int,int>,double> Map;
And you want to use Map[make_pair(i,j)]=value or Map.find(make_pair(i,j)) to operate on the map. Then you'll have to tell the system how to hash a pair of integers make_pair(i,j). Instead of that, we can define
inline size_t key(int i,int j) {return (size_t) i << 32 | (unsigned int) j;}
and then change the type of the map to
unordered_map<size_t,double> Map;
We can now use Map[key(i,j)]=value or Map.find(key(i,j)) to operate on the map. Every make_pair now becomes calling the inline key function.
This method guarantees that the key will be optimally hashed, because now the hashing part is done by the system, which will always choose the internal hash table size to be prime to make sure every bucket is equally likely. But you have to make yourself 100% sure that the key is unique for every pair, i.e., no two distinct pairs can have the same key, or there can be very difficult bugs to find.
If using pair is not a strict requirement, you can simply use map twice.
#include <unordered_map>
using namespace std;
using Unordered_map = unordered_map<string, unordered_map<string, int>>;
Unordered_map um;
um["Region1"]["Candidate1"] = 10;
cout << um["Region1"]["Candidate1"]; // 10
For pair key, we can use boost pair hash function:
#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
using namespace std;
int main() {
unordered_map<pair<string, string>, int, boost::hash<pair<string, string>>> m;
m[make_pair("123", "456")] = 1;
cout << m[make_pair("123", "456")] << endl;
return 0;
}
Similarly we can use boost hash for vectors,
#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
#include <vector>
using namespace std;
int main() {
unordered_map<vector<string>, int, boost::hash<vector<string>>> m;
vector<string> a({"123", "456"});
m[a] = 1;
cout << m[a] << endl;
return 0;
}
Reference: C++ Standard Library: A tutorial and reference, Second version Chapter 7.9.2: Creating and Controlling unordered Container
All solutions I found in Google use XOR to generate hashcode of pair, which is totally bad. see why-is-xor-the-default-way-to-combine-hashes. However, the book has given us the best solution, using hash_combine, which is taken from Boost. The solution is much better than XOR when I tested it in Online Judge(Atcoder). I organized the code as a template as follow. You can copy and paste it as much as you can. And it is convenient to change it to fit any custom struct/class.
Update: add hash template for tuple.
#include <functional>
namespace hash_tuple {
template <typename TT> struct hash {
size_t operator()(TT const &tt) const { return std::hash<TT>()(tt); }
};
// from boost (functional/hash):
// see http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html template
template <class T> inline void hash_combine(std::size_t &seed, T const &v) {
seed ^= hash_tuple::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
// Recursive template code derived from Matthieu M.
template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>
struct HashValueImpl {
void operator()(size_t &seed, Tuple const &tuple) const {
HashValueImpl<Tuple, Index - 1>{}(seed, tuple);
hash_combine(seed, std::get<Index>(tuple));
}
};
template <class Tuple> struct HashValueImpl<Tuple, 0> {
void operator()(size_t &seed, Tuple const &tuple) const {
hash_combine(seed, std::get<0>(tuple));
}
};
template <typename... TT> struct hash<std::tuple<TT...>> {
size_t operator()(std::tuple<TT...> const &tt) const {
size_t seed = 0;
HashValueImpl<std::tuple<TT...>>{}(seed, tt);
return seed;
}
};
// auxiliary generic functions to create a hash value using a seed
template <typename T> inline void hash_val(std::size_t &seed, const T &val) {
hash_combine(seed, val);
}
template <typename T, typename... Types>
inline void hash_val(std::size_t &seed, const T &val, const Types &... args) {
hash_combine(seed, val);
hash_val(seed, args...);
}
template <typename... Types>
inline std::size_t hash_val(const Types &... args) {
std::size_t seed = 0;
hash_val(seed, args...);
return seed;
}
struct pair_hash {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2> &p) const {
return hash_val(p.first, p.second);
}
};
} // namespace hash_tuple
#include <bits/stdc++.h>
int main() {
using ll = long long;
// std::unordered_map<std::pair<ll, ll>, ll, hash_tuple::pair_hash>
// hashmapPair; std::unordered_set<std::pair<ll, ll>, hash_tuple::pair_hash>
// hashsetPair;
std::unordered_map<std::pair<ll, ll>, ll, hash_tuple::pair_hash>
hashmapPair;
hashmapPair[{0, 0}] = 10;
std::unordered_set<std::pair<ll, ll>, hash_tuple::pair_hash> hashsetPair;
hashsetPair.insert({1, 1});
using TI = std::tuple<ll, ll, ll, ll>;
std::unordered_map<TI, ll, hash_tuple::hash<TI>> hashmapTuple;
hashmapTuple[{0, 1, 2, 3}] = 10;
std::unordered_set<TI, hash_tuple::hash<TI>> hashsetTuple;
hashsetTuple.emplace(0, 1, 2, 3);
return 0;
}
As your compilation error indicates, there is no valid instantiation of std::hash<std::pair<std::string, std::string>> in your std namespace.
According to my compiler:
Error C2338 The C++ Standard doesn't provide a hash for this
type. c:\program files (x86)\microsoft visual studio
14.0\vc\include\xstddef 381
You can provide your own specialization for std::hash<Vote> as follows:
#include <string>
#include <unordered_map>
#include <functional>
using namespace std;
using Vote = pair<string, string>;
using Unordered_map = unordered_map<Vote, int>;
namespace std
{
template<>
struct hash<Vote>
{
size_t operator()(Vote const& v) const
{
// ... hash function here ...
}
};
}
int main()
{
Unordered_map m;
}
In the comments on the answer by Baum mit Augen, the user Joe Black asked for an example on using a lambda expressions instead of defining a hash function. I agree with the opinion of Baum mit Augen, that this might harm readability, especially if you want to implement a more universal solution. Therefore, I'd like to keep my example short by focusing on a specific solution for std::pair<std::string, std::string>, as presented by the OP. The example also uses a handcrafted combination of std::hash<std::string> function calls:
using Vote = std::pair<std::string, std::string>;
auto hash = [](const Vote& v){
return std::hash<std::string>()(v.first) * 31 + std::hash<std::string>()(v.second);
};
using Unordered_map = std::unordered_map<Vote, int, decltype(hash)>;
Unordered_map um(8, hash);
Code on Ideone
I've simplified #YoungForest's answer to only work with pairs (= not with arbitrary length tuples) as was requested by the OP. I've also minimized the boilerplate code:
#include <functional>
#include <iostream>
#include <unordered_map>
#include <utility> # pair
using namespace std;
// from boost (functional/hash):
// see http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html template
template <class T> inline void hash_combine(size_t &seed, T const &v) {
seed ^= hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
struct pair_hash {
template <class T1, class T2>
size_t operator()(const pair<T1, T2> &p) const {
size_t seed = 0;
hash_combine(seed, p.first);
hash_combine(seed, p.second);
return seed;
}
};
int main() {
unordered_map<pair<int, int>, int, pair_hash> d;
d[{1, 2}] = 3;
cout << d.find({1, 2})->second << endl;
return 0;
}
It uses the same logic as in the boost library (that is better than the xor version).
There is a hack to such problems
Use a std:unordered_map of string
Look at the following example-
I am required to hash the endpoint(corner) of a rectangle
Error Approach
unordered_map<pair<int, int>, int> M; //ERROR
pair<int, int> p;
M[p]++;
Hack
unordered_map<string, int> M;
pair<int, int> p;
string s = to_string(p.first) + "_" + to_string(p.second);
M[s]++;
Such hack even works if you are required to create a hash of decimal or double as a key :)

Why can't I compile an unordered_map with a pair as key?

I am trying to create an unordered_map to map pairs with integers:
#include <unordered_map>
using namespace std;
using Vote = pair<string, string>;
using Unordered_map = unordered_map<Vote, int>;
I have a class where I have declared an Unordered_map as a private member.
However, I am getting the following error when I try to compile it:
/Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/include/c++/v1/type_traits:948:38: Implicit instantiation of undefined template 'std::__1::hash, std::__1::basic_string > >'
I am not getting this error if I use a regular map like map<pair<string, string>, int> instead of an unordered_map.
Is it not possible to use pair as key in unordered maps?
You need to provide a suitable hash function for your key type. A simple example:
#include <unordered_map>
#include <functional>
#include <string>
#include <utility>
// Only for pairs of std::hash-able types for simplicity.
// You can of course template this struct to allow other hash functions
struct pair_hash {
template <class T1, class T2>
std::size_t operator () (const std::pair<T1,T2> &p) const {
auto h1 = std::hash<T1>{}(p.first);
auto h2 = std::hash<T2>{}(p.second);
// Mainly for demonstration purposes, i.e. works but is overly simple
// In the real world, use sth. like boost.hash_combine
return h1 ^ h2;
}
};
using Vote = std::pair<std::string, std::string>;
using Unordered_map = std::unordered_map<Vote, int, pair_hash>;
int main() {
Unordered_map um;
}
This will work, but not have the best hash-properties†. You might want to have a look at something like boost.hash_combine for higher quality results when combining the hashes. This is also discussed in greater detail – including the aforementioned solution from boost – in this answer.
For real world use: Boost also provides the function set hash_value which already provides a hash function for std::pair, as well as std::tuple and most standard containers.
†More precisely, it will produce too many collisions. E.g., every symmetric pair will hash to 0 and pairs that differ only by permutation will have the same hash. This is probably fine for your programming exercise, but might seriously hurt performance of real world code.
My preferred way of solving this problem is to define a key function that transforms your pair into a unique integer (or any hashable data type). This key is not the hash key. It is the unique ID of the pair of data that will then be optimally hashed by the unordered_map. For example, you wanted to define an unordered_map of the type
unordered_map<pair<int,int>,double> Map;
And you want to use Map[make_pair(i,j)]=value or Map.find(make_pair(i,j)) to operate on the map. Then you'll have to tell the system how to hash a pair of integers make_pair(i,j). Instead of that, we can define
inline size_t key(int i,int j) {return (size_t) i << 32 | (unsigned int) j;}
and then change the type of the map to
unordered_map<size_t,double> Map;
We can now use Map[key(i,j)]=value or Map.find(key(i,j)) to operate on the map. Every make_pair now becomes calling the inline key function.
This method guarantees that the key will be optimally hashed, because now the hashing part is done by the system, which will always choose the internal hash table size to be prime to make sure every bucket is equally likely. But you have to make yourself 100% sure that the key is unique for every pair, i.e., no two distinct pairs can have the same key, or there can be very difficult bugs to find.
If using pair is not a strict requirement, you can simply use map twice.
#include <unordered_map>
using namespace std;
using Unordered_map = unordered_map<string, unordered_map<string, int>>;
Unordered_map um;
um["Region1"]["Candidate1"] = 10;
cout << um["Region1"]["Candidate1"]; // 10
For pair key, we can use boost pair hash function:
#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
using namespace std;
int main() {
unordered_map<pair<string, string>, int, boost::hash<pair<string, string>>> m;
m[make_pair("123", "456")] = 1;
cout << m[make_pair("123", "456")] << endl;
return 0;
}
Similarly we can use boost hash for vectors,
#include <iostream>
#include <boost/functional/hash.hpp>
#include <unordered_map>
#include <vector>
using namespace std;
int main() {
unordered_map<vector<string>, int, boost::hash<vector<string>>> m;
vector<string> a({"123", "456"});
m[a] = 1;
cout << m[a] << endl;
return 0;
}
Reference: C++ Standard Library: A tutorial and reference, Second version Chapter 7.9.2: Creating and Controlling unordered Container
All solutions I found in Google use XOR to generate hashcode of pair, which is totally bad. see why-is-xor-the-default-way-to-combine-hashes. However, the book has given us the best solution, using hash_combine, which is taken from Boost. The solution is much better than XOR when I tested it in Online Judge(Atcoder). I organized the code as a template as follow. You can copy and paste it as much as you can. And it is convenient to change it to fit any custom struct/class.
Update: add hash template for tuple.
#include <functional>
namespace hash_tuple {
template <typename TT> struct hash {
size_t operator()(TT const &tt) const { return std::hash<TT>()(tt); }
};
// from boost (functional/hash):
// see http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html template
template <class T> inline void hash_combine(std::size_t &seed, T const &v) {
seed ^= hash_tuple::hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
// Recursive template code derived from Matthieu M.
template <class Tuple, size_t Index = std::tuple_size<Tuple>::value - 1>
struct HashValueImpl {
void operator()(size_t &seed, Tuple const &tuple) const {
HashValueImpl<Tuple, Index - 1>{}(seed, tuple);
hash_combine(seed, std::get<Index>(tuple));
}
};
template <class Tuple> struct HashValueImpl<Tuple, 0> {
void operator()(size_t &seed, Tuple const &tuple) const {
hash_combine(seed, std::get<0>(tuple));
}
};
template <typename... TT> struct hash<std::tuple<TT...>> {
size_t operator()(std::tuple<TT...> const &tt) const {
size_t seed = 0;
HashValueImpl<std::tuple<TT...>>{}(seed, tt);
return seed;
}
};
// auxiliary generic functions to create a hash value using a seed
template <typename T> inline void hash_val(std::size_t &seed, const T &val) {
hash_combine(seed, val);
}
template <typename T, typename... Types>
inline void hash_val(std::size_t &seed, const T &val, const Types &... args) {
hash_combine(seed, val);
hash_val(seed, args...);
}
template <typename... Types>
inline std::size_t hash_val(const Types &... args) {
std::size_t seed = 0;
hash_val(seed, args...);
return seed;
}
struct pair_hash {
template <class T1, class T2>
std::size_t operator()(const std::pair<T1, T2> &p) const {
return hash_val(p.first, p.second);
}
};
} // namespace hash_tuple
#include <bits/stdc++.h>
int main() {
using ll = long long;
// std::unordered_map<std::pair<ll, ll>, ll, hash_tuple::pair_hash>
// hashmapPair; std::unordered_set<std::pair<ll, ll>, hash_tuple::pair_hash>
// hashsetPair;
std::unordered_map<std::pair<ll, ll>, ll, hash_tuple::pair_hash>
hashmapPair;
hashmapPair[{0, 0}] = 10;
std::unordered_set<std::pair<ll, ll>, hash_tuple::pair_hash> hashsetPair;
hashsetPair.insert({1, 1});
using TI = std::tuple<ll, ll, ll, ll>;
std::unordered_map<TI, ll, hash_tuple::hash<TI>> hashmapTuple;
hashmapTuple[{0, 1, 2, 3}] = 10;
std::unordered_set<TI, hash_tuple::hash<TI>> hashsetTuple;
hashsetTuple.emplace(0, 1, 2, 3);
return 0;
}
As your compilation error indicates, there is no valid instantiation of std::hash<std::pair<std::string, std::string>> in your std namespace.
According to my compiler:
Error C2338 The C++ Standard doesn't provide a hash for this
type. c:\program files (x86)\microsoft visual studio
14.0\vc\include\xstddef 381
You can provide your own specialization for std::hash<Vote> as follows:
#include <string>
#include <unordered_map>
#include <functional>
using namespace std;
using Vote = pair<string, string>;
using Unordered_map = unordered_map<Vote, int>;
namespace std
{
template<>
struct hash<Vote>
{
size_t operator()(Vote const& v) const
{
// ... hash function here ...
}
};
}
int main()
{
Unordered_map m;
}
In the comments on the answer by Baum mit Augen, the user Joe Black asked for an example on using a lambda expressions instead of defining a hash function. I agree with the opinion of Baum mit Augen, that this might harm readability, especially if you want to implement a more universal solution. Therefore, I'd like to keep my example short by focusing on a specific solution for std::pair<std::string, std::string>, as presented by the OP. The example also uses a handcrafted combination of std::hash<std::string> function calls:
using Vote = std::pair<std::string, std::string>;
auto hash = [](const Vote& v){
return std::hash<std::string>()(v.first) * 31 + std::hash<std::string>()(v.second);
};
using Unordered_map = std::unordered_map<Vote, int, decltype(hash)>;
Unordered_map um(8, hash);
Code on Ideone
I've simplified #YoungForest's answer to only work with pairs (= not with arbitrary length tuples) as was requested by the OP. I've also minimized the boilerplate code:
#include <functional>
#include <iostream>
#include <unordered_map>
#include <utility> # pair
using namespace std;
// from boost (functional/hash):
// see http://www.boost.org/doc/libs/1_35_0/doc/html/hash/combine.html template
template <class T> inline void hash_combine(size_t &seed, T const &v) {
seed ^= hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
struct pair_hash {
template <class T1, class T2>
size_t operator()(const pair<T1, T2> &p) const {
size_t seed = 0;
hash_combine(seed, p.first);
hash_combine(seed, p.second);
return seed;
}
};
int main() {
unordered_map<pair<int, int>, int, pair_hash> d;
d[{1, 2}] = 3;
cout << d.find({1, 2})->second << endl;
return 0;
}
It uses the same logic as in the boost library (that is better than the xor version).
There is a hack to such problems
Use a std:unordered_map of string
Look at the following example-
I am required to hash the endpoint(corner) of a rectangle
Error Approach
unordered_map<pair<int, int>, int> M; //ERROR
pair<int, int> p;
M[p]++;
Hack
unordered_map<string, int> M;
pair<int, int> p;
string s = to_string(p.first) + "_" + to_string(p.second);
M[s]++;
Such hack even works if you are required to create a hash of decimal or double as a key :)