How do I specify a custom hash function explicitly for unordered_set by passing a named function?

五迷三道 提交于 2020-12-30 11:54:47

问题


Based on the accepted answer to this question, one can use a specialization to std to provide a hash function for a user defined type.

#include <unordered_set>
#include <stdint.h>


struct FooBar {
    int i; 
};
namespace std {
    template <> struct hash<FooBar>
    {
        size_t operator()(const FooBar & x) const
        {
            return x.i;
        }
    };
}

int main(){
    std::unordered_set<FooBar> foo(0);
}

However, the documentation seems to imply that a custom hash function can also be passed explicitly into the constructor, and I would like to use a named function for this hash function.

However, my current attempt suffers from compile errors.

#include <unordered_set>
#include <stdint.h>

struct FooBar {
    int i; 
};

const size_t hashFooBar(const FooBar& foo) {
    return foo.i;
}

int main(){
    std::unordered_set<FooBar> foo(0, hashFooBar);
}

What is the correct template magic and method signature to make this work?


回答1:


You need to supply the type of the hasher, which is in your case a function pointer. And your FooBar type must be equality comparable. Or equivalently you could supply a equality predicate in the same manner as supplying the hasher.

#include <unordered_set>
#include <stdint.h>

struct FooBar {
    int i; 
};

bool operator==(const FooBar& x, const FooBar& y)
{
    return x.i == y.i;
}

size_t hashFooBar(const FooBar& foo) {
    return foo.i;
}

int main(){
    std::unordered_set<FooBar, size_t(*)(const FooBar&)> foo(0, hashFooBar);
}

I should also note that it is more popular to supply a "functor" instead of a function, as the former can be inlined, while the latter is likely to not be inlined.

#include <unordered_set>
#include <stdint.h>

struct FooBar {
    int i; 
};

bool operator==(const FooBar& x, const FooBar& y)
{
    return x.i == y.i;
}

struct hashFooBar
{
    size_t operator()(const FooBar& foo) const {
        return foo.i;
    }
};

int main(){
    std::unordered_set<FooBar, hashFooBar> foo(0);
}



回答2:


In addition to Howard Hinnant's answer, which explains how to pass in both a function pointer and a custom preferred (the latter strictly preferred), you can also pass in a lambda like so:

bool operator==(const FooBar& x, const FooBar& y)
{
    return x.i == y.i;
}

int main() {
    auto hash = [](const FooBar& foo) { return foo.i; };
    std::unordered_set<FooBar, decltype(hash)> set{0, hash};
}

This will also likely inline the hash function, whereas the function pointer version definitely won't. You can also see that by just printing the sizes:

std::unordered_set<FooBar, decltype(hash)> setLambda{0, hash};
std::unordered_set<FooBar, int(*)(const FooBar&)> setFuncPtr{0, +hash};

std::cout << sizeof(setLambda);   // prints 56
std::cout << sizeof(setFuncPtr);  // prints 64, cause of the
                                  // extra function pointer


来源:https://stackoverflow.com/questions/28120908/how-do-i-specify-a-custom-hash-function-explicitly-for-unordered-set-by-passing

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!