hash_map is not part of the current C++ STL, but is universally adopted as an extension. Using hash_map is simple when using built-in primitives like int or char*. Attempting to use user-defined classes as a key is somewhat more difficult, as the following example demonstrates:
class example1 {
public:
example1() {};
~example1() {};
uint8_t name_[8];
};
typedef __gnu_cxx::hash_map<example1,int> Example1HashType;
Example1HashType hash_map1;
int main(int argc, char **argv) {
example1 e1;
hash_map1.insert(std::make_pair(e1, 1));
}
When compiled with gcc 4.2 this results in:
/usr/include/c++/4.2/bits/stl_function.h:200: error: no match for ‘operator==’ in ‘__x == __y’
/usr/include/c++/4.2/ext/hashtable.h:595: error: no match for call to ‘(const __gnu_cxx::hash<example1>) (const example1&)’
hash_map requires two things: a hash function, and an ability to compare equality. To use a class as a key you need to provide a hash<> template specialized for the class in question. You also need to implement an equality operator.
class example1 {
public:
example1() {};
~example1() {};
bool operator==(const example1 &other) const {
return (memcmp(name_, other.name_, sizeof(name_)) == 0);
};
uint8_t name_[8];
};
namespace __gnu_cxx {
template<> struct hash<example1> {
size_t operator()(const example1& k) const {
size_t hashval = 0;
for (int i = 0; i < sizeof(k.name_); ++i) {
hashval = 5 * hashval + k.name_[i];
}
return hashval;
}
};
} // namespace __gnu_cxx
typedef __gnu_cxx::hash_map<example1,int> Example1HashType;
int main(int argc, char **argv) {
example1 e1;
Example1HashType hash_map1;
hash_map1.insert(std::make_pair(e1, 1));
}
This works, but what if we cannot add an operator method? For example, perhaps the class is in a library which we cannot modify, or is created by a code generator. Consider this case where key is a simple struct with no member functions. This code fails to compile, due to lack of "__x == __y"
struct example2 {
uint8_t name_[8];
};
namespace __gnu_cxx {
template<> struct hash<example2> {
size_t operator()(const example2& k) const {
size_t hashval = 0;
for (int i = 0; i < sizeof(k.name_); ++i) {
hashval = 5 * hashval + k.name_[i];
}
return hashval;
}
};
} // namespace __gnu_cxx
typedef __gnu_cxx::hash_map<example2,int> Example2HashType;
Example2HashType hash_map2;
int main(int argc, char **argv) {
example2 e2;
hash_map2.insert(std::make_pair(e2, 1));
}
As this is C++, the solution will of course involve more templates. hash_map does not directly invoke "x == y," it uses an equal_to<> template. The "__x == __y" compiler error is from the template. We can provide an equal_to<> specialization instead.
struct example2 {
uint8_t name_[8];
};
namespace std {
template<> struct equal_to<example2> {
bool operator()(const example2& x, const example2& y) const {
return (memcmp(x.name_, y.name_, sizeof(x.name_)) == 0);
}
};
} // namespace std
namespace __gnu_cxx {
template<> struct hash<example2> {
size_t operator()(const example2& k) const {
size_t hashval = 0;
for (int i = 0; i < sizeof(k.name_); ++i) {
hashval = 5 * hashval + k.name_[i];
}
return hashval;
}
};
} // namespace __gnu_cxx
typedef __gnu_cxx::hash_map<example2,int> Example2HashType;
Example2HashType hash_map2;
int main(int argc, char **argv) {
example2 e2;
hash_map2.insert(std::make_pair(e2, 1));
}
Extending hash_map to custom key types makes it useful in far more situations.