c++lambdafunctional-programmingunordered-set

How to customize hash function for pair<int,int> in unordered_set using lambda expression


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); //Fine

    
    
    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); //Error
}

I wonder why unordered_set can not use a lambda expression for a customized hash function.

At first I use this:

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; //Fine

The above code run well. So I wanted to convert it to a lambda expression. But I failed. I just want to know how to write a proper lambda expression about pair<int,int> hash function for unordered_set.


Solution

  • So std::unordered_set does not have a matching constructor for what you are trying to do.

    You need to add an additional argument to blocks to specify the size of the set, i.e. the bucket_count: https://en.cppreference.com/w/cpp/container/unordered_set/unordered_set

    Like this:

    std::unordered_set<std::pair<int, int>, decltype(pair_hash)> blocks(3, pair_hash);
    

    Also, I don't think this is a lambda expression:

    function<size_t(pair<int, int>&)> pair_hash = [](pair<int, int>& p) -> size_t {
        return hash<int>()(p.first) ^ hash<int>()(p.second);
    };
    

    You create a lambda expression, but then assign it to a std::function type object.

    I wonder if this works instead:

    auto pair_hash = [](pair<int, int>& p) -> size_t {
        return hash<int>()(p.first) ^ hash<int>()(p.second);
    };
    

    Additionally, this function is not the same, since it takes a const & as a parameter. Perhaps you need to add a const qualification to your lambda?:

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