为什么`std :: hash`不被重载为`std :: begin`的自定义点?

时间:2019-09-08 03:19:23

标签: c++ hash overloading partial-specialization customization-point

正如标题所述,std::beginstd::endstd::swap等是众所周知的std“定制点”(功能应由ADL查找) 。但是,我认为std::hash是用户可定制的唯一标准行为,它暗示(A)打开std命名空间(B)创建局部特化。

为什么std::hash不能通过重载函数而不是部分地专门化类来像其他定制点那样设计?

2 个答案:

答案 0 :(得分:0)

std::lessstd::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设置为默认使用它,也可以提供自己的哈希函数对象。

答案 1 :(得分:0)

您被一个常见的误解所吸引,但是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通常是未定义的行为,另请参见What are the reasons that extending the std namespace is considered undefined behavior?在某些情况下模板专门化是个例外,但在这种情况下甚至没有必要。

#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;
}