C++11 元编程 判断是否有std::hash<T>特例并提供hash函数通用实现
来源:互联网 发布:seo sem 新媒体 电商 编辑:程序博客网 时间:2024/06/16 00:28
std::hash<T>
的用途
std::hash<T>
是C++11提供的一元函数模板,用于向标准库提供返回数据类型T哈希值(hash value)的哈希函数(hash function)。 std::hash<T>
只是定义了一个一元操作符operator()
,接受一个T类型的参数,返回一个size_t
类型的哈希值,
C++11为所有基本类型(basic types)都提供了特例化实现:
C++11标准库定义的类型也提供都有提供特例化实现:
自定义类型的std::hash<T>
特化
但是自定义的类型需要程序员自己定义std::hash<T>
的特例化实现
比如下面代码就为自定义类型struct S
提供 了std::hash<S>
特例化实现
struct S{ std::string first_name; std::string last_name;};/* 为S提供 std::hash<T>特例化实现 */namespace std{ template<> struct hash<S> { typedef S argument_type; typedef std::size_t result_type; result_type operator()(argument_type const& s) const { result_type const h1 ( std::hash<std::string>()(s.first_name) ); result_type const h2 ( std::hash<std::string>()(s.last_name) ); return h1 ^ (h2 << 1); } };}
为自定义类型提供std::hash<T>
特例化有什么用呢?
比如,如果你要使用上面的自定义类型struct S
作为std::unorderd_map的key,就必须为模板类提供Hash参数,也就是提供key的hash函数。下面是std::unorderd_map
的模板定义。
template < class Key, // unordered_map::key_type class T, // unordered_map::mapped_type class Hash = hash<Key>, // unordered_map::hasher class Pred = equal_to<Key>, // unordered_map::key_equal class Alloc = allocator< pair<const Key,T> > // unordered_map::allocator_type > class unordered_map;
我们一般像下面这样使用unordered_map
,不用提供Hash 参数,是因为对于string
,STL已经提供了string
的std::hash<T>
特例化实现
std::unordered_map<string,string> map;
hash函数的通用实现
有时在项目中有多个自定义类型需要提供std::hash<T>
特例化实现,为每个类型写一个特例化实现也挺烦的。
那么可以考虑提供一个hash函数的通用实现,并在编译期通过模板函数自动判断类型是否有std::hash<T>
的特例实现,如果有就使用T自己的特例化实现,如果没有就使用通用的hash函数实现,下面是实现代码,详细说明见代码中注释:
#include <iostream>#include <functional>#include <string>#include <type_traits>#include <unordered_map>using namespace std;/* TT没有std::hash<TT>特例化实现 */struct TT{const int t1=18354;};struct S{ std::string first_name; std::string last_name;};/* 为S提供 std::hash<T>特例化实现 */namespace std{ template<> struct hash<S> { typedef S argument_type; typedef std::size_t result_type; result_type operator()(argument_type const& s) const { result_type const h1 ( std::hash<std::string>()(s.first_name) ); result_type const h2 ( std::hash<std::string>()(s.last_name) ); return h1 ^ (h2 << 1); } };}/* 返回获取hash值的一元函数实现, * 如果T有std::hash<T>特例实现返回std::hash<T>,否则提供缺省的hash实现 */template<typename T>struct hash_fn{ /* 缺省的hash实现 */ struct default_hash { typedef T argument_type; typedef std::size_t result_type; result_type operator()(argument_type const& t) const noexcept { auto obj_size=sizeof(t); uint64_t hashcode = 0; uint64_t *p = (uint64_t*) std::addressof(t); uint64_t *const u = p + obj_size/sizeof(uint64_t); const int tail=obj_size % sizeof(uint64_t); const int shift = (sizeof(uint64_t) - sizeof(uint32_t)) << 3; //BKDRHash uint64_t seed = 131; // 31 131 1313 13131 131313 etc.. for (; p < u; p ++) hashcode = hashcode * seed + *p; if (tail) #if defined( _MSC_VER) || (defined(__GNUC__) && __BYTE_ORDER__ ==__ORDER_LITTLE_ENDIAN__) hashcode = hashcode * seed+ ((*p) &((1ULL<<(tail << 3))-1));// 小端模式 #elif defined(__GNUC__) && __BYTE_ORDER__ ==__ORDER_BIG_ENDIAN__ hashcode = hashcode * seed+ ((*p) >> ((sizeof(uint64_t) - tail) << 3)); // 大端模式 #else #error unexpected c complier (msc/gcc) #endif return (result_type) hashcode; } }; /* SFINAE 判断T有没有std::hash<T>特例实现 */ template<typename U> static std::hash<U> test(decltype(declval<std::hash<U>>().operator()(declval<U>()))); template<typename U> static default_hash test(...); //如果T没有std::hash<T>特例化实现,则type的类型为default_hash //否则type类型为std::hash<T> using type =decltype(test<T>(0)); type fn;};int main(){ S s; s.first_name = "Bender"; s.last_name = "Rodriguez"; cout<<hash_fn<TT>().fn(TT())<<endl; cout<<hash_fn<S>().fn(s)<<endl; //S有std::hash<S>特例实现,无需指定std::unordered_map的Hash参数 std::unordered_map<S,string> map_s; //TT没有std::hash<TT>实现,将hash_fn<TT>的计算结果作为Hash参数, //hash_fn<TT>::type会自动选择缺省的哈希实现 std::unordered_map<TT,string,typename hash_fn<TT>::type> map_tt;}
判断std::hash<T>
是否实现的元函数
另外,还可以单独写一个元函数来判断类型T是否有std::hash<T>
特例
#include <iostream>#include <functional>#include <string>#include <type_traits>/* 判断有没有std::hash<T>实现 */template <typename T>struct has_hash_specific{ template<typename U> static auto test(int)-> decltype(declval<std::hash<U>>().operator()(declval<U>())); template<typename U> static void test(...); enum{value=!std::is_void<decltype(test<T>(0))>::value}; //通过判断test<T>(0)返回值是否为void来判断是否有hash<T>特例};struct TT{const int t1=18354;};struct S{ std::string first_name; std::string last_name;};namespace std{ template<> struct hash<S> { typedef S argument_type; typedef std::size_t result_type; result_type operator()(argument_type const& s) const { result_type const h1 ( std::hash<std::string>()(s.first_name) ); result_type const h2 ( std::hash<std::string>()(s.last_name) ); return h1 ^ (h2 << 1); } };}int main(){ S s; s.first_name = "Bender"; s.last_name = "Rodriguez"; cout<<"has_hash_spec<S>"<<has_hash_specific<S>::value<<endl; cout<<"has_hash_spec<int>"<<has_hash_specific<int>::value<<endl; cout<<"has_hash_spec<uint64_t>"<<has_hash_specific<uint64_t>::value<<endl; cout<<"has_hash_spec<TT>"<<has_hash_specific<TT>::value<<endl;}
运行结果
has_hash_spec<S>1has_hash_spec<int>1has_hash_spec<uint64_t>1has_hash_spec<TT>0
注意:
default_hash其实只能用于成员为基本类型的class/union/struct,对于包含复杂对象的类型还是需要提供std::hash<T>
特例化实现
- C++11 元编程 判断是否有std::hash<T>特例并提供hash函数通用实现
- C++11 元编程(meta-programming)判断T是否有==操作符
- C语言hash函数
- std:sort和字符串hash函数
- hash函数实现
- hash函数实现
- scala实现Hash函数
- 各种hash 函数实现
- HASH的dig方法判断key是否存在及是否有值
- c++11 std::hash 的使用
- Hash 函数、Hash表
- Java实现的hash算法实现大全,供大家参考
- 经典的字符串hash函数C/java实现
- hash表 c语言实现
- hash函数
- hash函数
- hash函数
- hash函数
- iOS开发根本布景介绍
- 记录一次“任意文件下载”高危漏洞
- WebService:简介
- 字节对齐
- NumPy学习 -- 001_数组
- C++11 元编程 判断是否有std::hash<T>特例并提供hash函数通用实现
- Linux日志系统(Logcheck)的安装预配置
- ActiveMQ的安全配置
- 图解Android - Zygote, System Server 启动分析
- php foreach 意外情况简述
- 测试
- 安装g++
- WebService:JDK发布service
- Oracle数据库-建库、建表空间,建用户