C++20透明哈希配置后,unordered_set为何无法按std::pair搜索?
问题:C++20透明哈希无法直接用
std::pair搜索unordered_set的原因 需求背景
我们有自定义结构体Pixel2DWithCount,需要实现两个目标:
- 将像素存储在
unordered_set中 - 直接通过两个整数值在集合中搜索
C++20之前的实现
只能通过创建临时Pixel2DWithCount对象进行搜索,代码如下:
#include <functional> #include <iostream> #include <unordered_set> struct Pixel2DWithCount { int x, y; Pixel2DWithCount(int x_, int y_): x(x_), y(y_){} inline friend bool operator==(const Pixel2DWithCount &first, const Pixel2DWithCount &second) noexcept{ return first.x == second.x && first.y == second.y; } }; struct Pixel2DWithCountHasher { std::hash<long> _hasher; size_t operator()(const Pixel2DWithCount& p) const { constexpr size_t _shift_bits_num = sizeof(int) * 8; return _hasher(static_cast<long>(p.x) << _shift_bits_num | static_cast<long>(p.y)); } }; int main(){ Pixel2DWithCount p1(1, 2); Pixel2DWithCount p2(2, 2); std::unordered_set<Pixel2DWithCount, Pixel2DWithCountHasher> s{p1, p2}; std::cout<<(s.find(p1) != s.end()); }
这种方式存在临时对象创建的性能开销,因此希望在C++20中利用透明哈希特性直接通过std::pair<int, int>搜索。
C++20的尝试代码
为支持透明哈希,实现了兼容Pixel2DWithCount和std::pair<int, int>的哈希器和比较器,但调用find(std::pair<int, int>)时编译器报错:no matching function for call to std::unordered_set<Pixel2DWithCount, Pixel2DWithCountHasher, ComparePixel2DWithCount>::find(std::pair<int, int>)。尝试的代码如下:
struct ComparePixel2DWithCount{ bool operator()(const Pixel2DWithCount &first, const Pixel2DWithCount &second) const noexcept{ return first.x == second.x && first.y == second.y; } bool operator()(const std::pair<int, int> &first, const Pixel2DWithCount &second) const noexcept{ return first.first == second.x && first.second == second.y; } bool operator()(const Pixel2DWithCount &first, const std::pair<int, int> &second) const noexcept{ return first.x == second.first && first.y == second.second; } }; struct Pixel2DWithCountHasher { std::hash<long> _hasher; size_t operator()(const std::pair<int, int>& pair) const { constexpr size_t _shift_bits_num = sizeof(int) * 8; return _hasher(static_cast<long>(pair.first) << _shift_bits_num | static_cast<long>(pair.second)); } size_t operator()(const Pixel2DWithCount& p) const { constexpr size_t _shift_bits_num = sizeof(int) * 8; return _hasher(static_cast<long>(p.x) << _shift_bits_num | static_cast<long>(p.y)); } }; int main(){ Pixel2DWithCount p1(1, 2); Pixel2DWithCount p2(2, 2); std::unordered_set<Pixel2DWithCount, Pixel2DWithCountHasher, ComparePixel2DWithCount> s{p1, p2}; // 此处调用find会报错 // s.find(std::make_pair(1,2)); }
错误原因
要让unordered_set支持透明哈希的异构查找,必须满足两个关键条件:
- 哈希器必须声明
is_transparent类型:需要在Pixel2DWithCountHasher结构体中添加using is_transparent = void;,告诉标准库该哈希器支持透明操作,可处理不同类型的输入。 - 比较器同样需要声明
is_transparent类型:在ComparePixel2DWithCount结构体中添加using is_transparent = void;,确保标准库识别到比较器支持跨类型比较。
标准库的unordered_set::find只有在哈希器和比较器都提供了is_transparent类型时,才会启用异构查找的重载版本。你的代码中缺少这两个类型定义,因此编译器无法找到接受std::pair<int, int>的find重载。
修正后的代码示例
在哈希器和比较器中添加is_transparent声明即可解决问题:
struct ComparePixel2DWithCount{ using is_transparent = void; // 添加透明声明 bool operator()(const Pixel2DWithCount &first, const Pixel2DWithCount &second) const noexcept{ return first.x == second.x && first.y == second.y; } bool operator()(const std::pair<int, int> &first, const Pixel2DWithCount &second) const noexcept{ return first.first == second.x && first.second == second.y; } bool operator()(const Pixel2DWithCount &first, const std::pair<int, int> &second) const noexcept{ return first.x == second.first && first.y == second.second; } }; struct Pixel2DWithCountHasher { using is_transparent = void; // 添加透明声明 std::hash<long> _hasher; size_t operator()(const std::pair<int, int>& pair) const { constexpr size_t _shift_bits_num = sizeof(int) * 8; return _hasher(static_cast<long>(pair.first) << _shift_bits_num | static_cast<long>(pair.second)); } size_t operator()(const Pixel2DWithCount& p) const { constexpr size_t _shift_bits_num = sizeof(int) * 8; return _hasher(static_cast<long>(p.x) << _shift_bits_num | static_cast<long>(p.y)); } }; int main(){ Pixel2DWithCount p1(1, 2); Pixel2DWithCount p2(2, 2); std::unordered_set<Pixel2DWithCount, Pixel2DWithCountHasher, ComparePixel2DWithCount> s{p1, p2}; // 现在可以直接用pair搜索 if (s.find(std::make_pair(1,2)) != s.end()) { std::cout << "Found pixel (1,2)\n"; } }
内容的提问来源于stack exchange,提问作者Ruotong Jia
相关产品推荐
相关产品推荐

