《STL源码剖析》的5.7.7 hash function一节中介绍了<stl_hash_fun.h>中定义了数个现成的hash函数,全都是仿函数。这些hash函数支持的模板类型包括:char*, const char*, char, unsigned char, signed char, short, unsigned short, int , unsigned int, long, unsigned long。这些不同类型的hash函数是通过对以下类模板实例化得到的:
template<class T> struct hash { }
大部分内置的hash函数内部并没有做什么,只是返回原值,例如针对short的hash函数如下:
__STL_TEMPLATE_NULL struct hash<short> {
size_t operator() (short x) const { return x; }
};
对于字符串类型(char *和 const char *),hash函数内部调用了__stl_hash_string,源码如下:
inline size_t __stl_hash_string(const char *s) {
unsigned long h = 0;
for ( ; *s; ++s)
h = 5*h + *s;
return size_t(h);
}
__STL_TEMPLATE_NULL struct hash<char *> {
size_t operator() (const char * s) const {
return __stl_hash_string(s);
}
};
由此可见,SGI STL的hashtable无法处理除上述类型以外的元素,例如string, double, float等。也就是说,如果定义unordered_set<string>,并向其中添加string对象时,编译无法通过。因为unordered_set内部使用的就是hashtable,并且没有内置的hash函数来处理string类型。
unordered_set的类定义如下:
template <class Value,
class HashFcn = hash<Value>,
class EqualKey = equal_to<Value>,
class Alloc = alloc>
class unordered_set {
};
第二个模板参数指明了默认的hash函数,但当Value实例化为string时,找不到相应的hash<string>,编译报错, 要想 unordered_set支持string, 必须为string自定义hash函数。代码如下:
#include <unordered_set>
#include <iostream>
#include <string>
#include <assert.h>
using namespace std;
template<> //模板实例化
struct hash<string> {
size_t operator()(string str) {
unsigned long h = 0;
for(string::size_type i = 0; i != str.size(); i++) {
h = 5*h + str[i];
}
return size_t(h);
}
};
int main () {
unordered_multiset<string> ihs;
string str;
cout << ihs.bucket_count() << endl;
ihs.insert(string("abc"));
ihs.insert(string("def"));
str.assign("kkk");
ihs.insert(str);
ihs.insert("abc");
assert(ihs.find(str) != ihs.end());
for(unordered_multiset<string>::iterator it = ihs.begin(); it != ihs.end(); it++) {
cout << *it << endl;
}
system("pause");
}