为何用Lambda为unordered_set的pair<int,int>自定义哈希函数报错?如何解决?
为什么unordered_set无法直接用Lambda作为哈希函数?
问题描述
在C++代码中,priority_queue使用Lambda表达式作为比较函数可以正常运行,但同样用Lambda定义哈希函数时,unordered_set却会编译报错。
出错代码示例:
int main(int argc, const char * argv[]) { function<bool(vector<int>&,vector<int>&)> cmp=[](vector<int>&a,vector<int>&b)->bool{ return a[0]>b[0]; }; priority_queue<vector<int>,vector<vector<int>>,decltype(cmp)> q(cmp); //运行正常 function<size_t(pair<int, int>&)> pair_hash = [](pair<int, int>& p) -> size_t { return hash<int>()(p.first) ^ hash<int>()(p.second); }; unordered_set<pair<int, int>, decltype(pair_hash)> blocks(pair_hash); //编译报错 }
而用结构体实现哈希函数时,unordered_set可以正常运行:
struct pair_hash { template <class T1, class T2> size_t operator () (pair<T1, T2> const &pair) const { size_t h1 = hash<T1>()(pair.first); size_t h2 = hash<T2>()(pair.second); return h1 ^ h2; } }; unordered_set<pair<int,int>, pair_hash> blocks; //运行正常
需要明确两个问题:unordered_set不能直接用Lambda做哈希函数的原因,以及用Lambda实现哈希函数的正确写法。
原因分析
- 容器对模板参数的要求不同:
priority_queue只要求比较函数的类型是可调用的,构造时传入Lambda实例就能正常工作。但unordered_set对哈希函数的类型有硬性要求:必须是可默认构造的——因为容器在内部操作(比如扩容)时,可能需要默认构造哈希函数的实例。而Lambda的类型(包括被std::function包裹的类型)不支持默认构造,每个Lambda都是独一无二的类型,没有默认构造函数。 - 结构体哈希的适配性:
你用的pair_hash结构体是普通类类型,自带默认构造函数,且operator()是模板化的,完全满足unordered_set对哈希函数的所有要求:可默认构造、可调用、const修饰。
正确的Lambda实现写法
写法1:C++20无捕获Lambda直接使用
C++20开始允许无捕获Lambda作为模板参数类型(因为无捕获Lambda的类型新增了默认构造函数),可以直接这么写:
int main(int argc, const char * argv[]) { // 无捕获Lambda,C++20支持作为unordered_set的哈希类型 auto pair_hash = [](const pair<int, int>& p) -> size_t { return hash<int>()(p.first) ^ hash<int>()(p.second); }; // 注意:必须显式指定初始桶数,再传入Lambda实例 // 否则编译器会把Lambda当成桶数参数,导致类型不匹配 unordered_set<pair<int, int>, decltype(pair_hash)> blocks(10, pair_hash); }
写法2:兼容C++11/17的包装方式
如果使用C11或C17,可以把Lambda放到一个可默认构造的载体里,或者用函数返回Lambda来推导类型:
// 定义返回哈希Lambda的函数 auto make_pair_hash() { return [](const pair<int, int>& p) -> size_t { return hash<int>()(p.first) ^ hash<int>()(p.second); }; } int main(int argc, const char * argv[]) { using HashType = decltype(make_pair_hash()); // 传入初始桶数和Lambda实例 unordered_set<pair<int, int>, HashType> blocks(10, make_pair_hash()); }
写法3:用std::function的兼容方案(不推荐)
如果一定要用std::function,必须确保构造unordered_set时传入有效的函数实例,避免默认构造空的std::function导致运行时崩溃:
int main(int argc, const char * argv[]) { function<size_t(const pair<int, int>&)> pair_hash = [](const pair<int, int>& p) -> size_t { return hash<int>()(p.first) ^ hash<int>()(p.second); }; // 必须显式传入桶数和哈希函数实例 unordered_set<pair<int, int>, decltype(pair_hash)> blocks(10, pair_hash); }
这种方式会有std::function类型擦除的性能损耗,不如直接用Lambda类型或结构体高效。
内容的提问来源于stack exchange,提问作者Celeglow Zhou
相关产品推荐
相关产品推荐

