I wanted to make a hash function for a class I am writing and I wanted to make the hash function a friend of the class so that I didn't have to write an unnecessary getter method. To do that I followed the accepted answer in this SO post. But I wanted to be able to insert the objects into either std::unordered_set
or boost::unordered_set
. So I wrote it like this:
#include <iostream>
#include <unordered_set>
#include <boost/functional/hash.hpp>
#include <boost/unordered_set.hpp>
class Vec;
namespace std { // line [c]
template<>
struct hash<Vec> {
public:
size_t operator()(const Vec &v) const;
};
}
class Vec {
private:
std::vector<int> v;
public:
friend size_t std::hash<Vec>::operator ()(const Vec& v) const; // line [d]
friend bool operator == (const Vec& lhs, const Vec& rhs) { return lhs.v == rhs.v; }
};
namespace std { // line [e]
size_t hash<Vec>::operator()(const Vec &v) const {
return boost::hash<std::vector<int> >()(v.v);
}
}
int main() {
Vec v;
std::unordered_set<Vec> s1; // line [f]
s1.insert(v); // line [g]
boost::unordered_set<Vec> s2; // line [a]
s2.insert(v); // line [b]
}
But I found that I had a long list of errors when trying to compile this. Then when I removed lines [a,b]
, it compiled and ran as expected. Then, instead of removing lines [a,b]
, I (1) left them in, (2) removed lines [f,g]
, and (3) changed lines [c,d,e]
to say boost
instead of std
, again the code would compile properly. Finally, I tried making a duplicate declaration of the hash struct in the boost
namespace:
#include <iostream>
#include <unordered_set>
#include <boost/functional/hash.hpp>
#include <boost/unordered_set.hpp>
class Vec;
namespace std {
template<>
struct hash<Vec> {
public:
size_t operator()(const Vec &v) const;
};
}
// new: {
namespace boost {
template<>
struct hash<Vec> {
public:
size_t operator()(const Vec &v) const;
};
}
// }
class Vec {
private:
std::vector<int> v;
public:
friend size_t std::hash<Vec>::operator ()(const Vec& v) const;
// new: {
friend size_t boost::hash<Vec>::operator ()(const Vec& v) const;
// }
friend bool operator == (const Vec& lhs, const Vec& rhs) { return lhs.v == rhs.v; }
};
namespace std {
size_t hash<Vec>::operator()(const Vec &v) const {
return boost::hash<std::vector<int> >()(v.v);
}
}
// new: {
namespace boost {
size_t hash<Vec>::operator()(const Vec &v) const {
return boost::hash<std::vector<int> >()(v.v);
}
}
// }
int main() {
Vec v;
std::unordered_set<Vec> s1;
s1.insert(v);
boost::unordered_set<Vec> s2;
s2.insert(v);
}
My question is: why do I have to make a hash function in both the std
and boost
namespace to get it to work? I would say that I have an intuition for why, but I would like a very detailed explanation. And I would like any alternative solutions that would fix the fact that there is a lot of duplicate code in the above code segment (but not something like boost::unordered_set<Vec, my_vec_class_hash>
because I want it to be "automatic").