只要我可以为我的类型提供哈希函数,为什么我需要为我的类型专门化 std::hash?

As long as I can provide a hasher function for my types why I need to specialize std::hash for my types?

只要我能为我的 class 类型提供我的 hasher 我为什么要专攻 std::hash<my_class_type>

我已经实现了这个例子:

class Foo;
template<>
class std::hash<Foo>;

class Foo
{
public:
    Foo(std::string const& s, int x) : str_(s), x_(x){}
    std::string str()const {return str_;}
    int get_x() const{ return x_;}

private:
    std::string str_;
    int x_{};
    friend bool operator==(Foo const&, Foo const&);
    friend class std::hash<Foo>;
};

bool operator==(Foo const& lhs, Foo const& rhs)
{
    return lhs.str_ == rhs.str_ && lhs.x_ == rhs.x_;
}

size_t hasher(Foo const& f)
{
    return std::hash<std::string>()(f.str()) ^
            std::hash<int>()(f.get_x());
}

namespace std
{
    template <>
    class hash<Foo>
    {
    public:
        using argument_type = Foo;
        using result_type = std::size_t;
        result_type operator()(argument_type const& a)const;
    };

    typename hash<Foo>::result_type
    hash<Foo>::operator()(argument_type const& a)const
    {
        return hash<std::string>()(a.str_) ^
                hash<int>()(a.x_);
    }
}

int main()
{

    std::unordered_set<Foo, decltype(hasher)*> u(5, hasher); // needs hasher as template argument and as function argument
    u.insert(Foo{"hi", 100});

    std::unordered_set<Foo> u2; // doesn't need either
    u2.insert(Foo("hi", 100));

    std::cout << "\ndone!\n";
}

As long as I can provide my hasher for my class types why should I [do I need to] specialize std::hash<my_class_type>?

不需要。无论如何,这里有一些这样做的原因:

  • 如果您的 class 是 API 的一部分(内部或外部无关紧要)并且您需要为 class 的用户提供将它用作无序容器中的 Key 的可能性(或出于任何其他原因,散列它),友好的方法是专门化 std::hash。没有人会期望必须指定一个特殊函数来对(可能记录为)可散列的类型进行散列。

  • 因为它并不比编写实际的哈希算法(只是一个小样板)多多少代码,如果你要多次使用它,你也可以为了你自己而做,或者两次。它使代码更清晰。

一旦你编写了特化,你就不需要自由散列函数,所以你可以立即特化 std::hash(并完全跳过自由散列函数)如果以上任何一个似乎申请。

使样板文件更短的一种方法是使用 struct 而不是 class 对其进行专门化。主要 class 模板是

namespace std {
    template<class Key>
    struct hash;
}

所以这样做也很有意义。您的定义将是:

namespace std {
    struct hash<Foo> {
        using argument_type = Foo;
        using result_type = std::size_t;
        result_type operator()(argument_type const& a) const;
    };
}

Is that for flexibility?

提供专业化不会影响灵活性,但确实会使其更易于使用。