我第一次使用std::unordered_set
并且对哈希函数有疑问。据我了解,如果您没有指定哈希函数,它将默认为std::hash<Key>
。
我的一个班级中有mySet
成员:
typedef std::unordered_set<MyClass> USetType;
USetType mySet;
当我尝试构建时,我收到以下错误:
错误C2440:'type cast':无法从'const MyClass'转换为'size_t'
如果要将size_t
与自定义类一起使用,是否需要定义转换函数(unordered_set
)?有没有办法避免编写自己的哈希函数并只使用默认值?
答案 0 :(得分:12)
如果你没有指定自己的哈希算子作为模板参数,它将默认为std::hash<MyClass>
,除非你定义它,否则它不存在。
最好定义您自己在std::hash
内的std
的专业化:
namespace std {
template <>
struct hash<MyClass>
{
typedef MyClass argument_type;
typedef std::size_t result_type;
result_type operator()(const MyClass & t) const
{
/* ..calculate hash value for t */
}
};
}
并确保在哈希声明之前包含此代码。这样,您可以将哈希声明为std::unordered_set<MyClass>
,而无需进一步的模板参数。
您没有指定MyClass
内部的内容,但典型情况是您的用户定义类型只包含几个简单类型成员,其中存在默认哈希函数。在这种情况下,您可能希望将各个类型的哈希值组合为整个组合的哈希值。为此,Boost库提供了一个名为hash_combine
的函数。当然,不能保证它在您的特定情况下能够很好地工作(它取决于数据值的分布和碰撞的可能性),但它提供了一个好的且易于使用的起点。
以下是如何使用它的示例,假设MyClass
由两个字符串成员组成:
#include <unordered_set>
#include <boost/functional/hash.hpp>
struct MyClass
{
std::string _s1;
std::string _s2;
};
namespace std {
template <>
struct hash<MyClass>
{
typedef MyClass argument_type;
typedef std::size_t result_type;
result_type operator()(const MyClass & t) const
{
std::size_t val { 0 };
boost::hash_combine(val,t._s1);
boost::hash_combine(val,t._s2);
return val;
}
};
}
int main()
{
std::unordered_set<MyClass> s;
/* ... */
return 0;
}
答案 1 :(得分:1)
我想扩展由 jogojapan 给出的answer。正如 CashCow 的comment中提到的那样,您还必须为operator==
使用overload the equality comparison operator(MyClass
)或定义一个单独的比较功能并将其提供给unordered_set
。否则,您将收到另一条错误消息。例如,VS 2013抛出:
错误C2678:二进制'==':未找到采用'const MyClass'类型的左操作数的运算符(或没有可接受的转换)
此外,您可以使用lambda expressions而不是定义哈希和比较函数。如果您不想使用 Boost ,那么也可以handcraft a hash function。我知道您想使用一些默认函数,但是编译器不知道如何为自定义类计算有意义的哈希。但是,您可以将std::hash
用于您的班级成员。
如果将所有内容放在一起,则您的代码可以编写如下:
class MyClass {
public:
int i;
double d;
std::string s;
};
int main()
{
auto hash = [](const MyClass& mc){
return (std::hash<int>()(mc.i) * 31 + std::hash<double>()(mc.d)) * 31 + std::hash<std::string>()(mc.s);
};
auto equal = [](const MyClass& mc1, const MyClass& mc2){
return mc1.i == mc2.i && mc1.d == mc2.d && mc1.s == mc2.s;
};
std::unordered_set<MyClass, decltype(hash), decltype(equal)> mySet(8, hash, equal);
return 0;
}