正如标题所说,std::begin
、std::end
、std::swap
等是众所周知的std
“定制点”(旨在由 ADL 找到的功能)。但是,std::hash
我认为,唯一可由用户自定义的 std 行为暗示 (A) 打开std
命名空间 (B) 创建部分专业化。
为什么没有std::hash
像其他的那样通过重载函数而不是部分特化类来设计定制点?
正如标题所说,std::begin
、std::end
、std::swap
等是众所周知的std
“定制点”(旨在由 ADL 找到的功能)。但是,std::hash
我认为,唯一可由用户自定义的 std 行为暗示 (A) 打开std
命名空间 (B) 创建部分专业化。
为什么没有std::hash
像其他的那样通过重载函数而不是部分特化类来设计定制点?
您被一个常见的误解所吸引,但std::hash
它不是用户定义类型散列的自定义点。相反,std::hash
它是标准容器使用的散列器的默认实现。如果您想使用具有用户定义类型的标准容器,那么您应该使用它们的模板参数来提供哈希。
所有内容需要哈希函数的标准容器都有一个模板参数,该参数接受一个函数对象,该函数对象的调用运算符计算哈希值。
#include <boost/functional/hash.hpp>
#include <unordered_set>
struct Point { int x, y; };
struct PointHasher {
size_t operator()(Point const &p) {
size_t seed = 0;
boost::hash_combine(seed, p.x);
boost::hash_combine(seed, p.y);
return seed;
}
};
int main() {
std::unordered_set<Point, PointHasher> m;
}
写入namespace std
通常是未定义的行为,另请参阅扩展 std 命名空间被视为未定义行为的原因是什么? 在某些情况下模板特化有一个例外,但在这种情况下甚至没有必要。
#include <boost/functional/hash.hpp>
#include <unordered_set>
struct Point { int x, y; };
namespace std {
template<> struct hash<Point> {
size_t operator()(Point const &p) {
size_t seed = 0;
boost::hash_combine(seed, p.x);
boost::hash_combine(seed, p.y);
return seed;
}
};
}
int main() {
std::unordered_set<point> m;
}
像std::less
,std::hash
意味着是一个函数对象。例如,std::unordered_set
接受一个Hash
模板参数:
template <
class Key ,
class Hash = std::hash<Key> , // function object type
class KeyEqual = std::equal_to<Key> ,
class Allocator = std::allocator<Key>
> class unordered_set;
这就是为什么std::hash
是类模板而不是函数模板的原因。您可以专门化std::hash
并unordered_set
默认使用它,或者您可以提供自己的哈希函数对象。