我希望unordered_map
有一个struct
,我希望将其用作多个std::set< std::string >
的密钥。
我看到自定义哈希函数is required并且字符串可以应用std::hash
;但是,我无法确定应该返回什么来满足unordered_map的这些集合的哈希函数的目的。
自定义散列函数应如何返回?
答案 0 :(得分:2)
我认为这可能是Snps' answer的更好选择。这为用户定义的类型实现了std::hash
的特化,并且它在不创建临时字符串的情况下散列结构。
我复制了Boost中的两个函数hash_combine
和hash_range
,以计算两个容器中的单个哈希值。
#include <iostream>
#include <functional>
#include <set>
#include <unordered_map>
// user-defined type
struct myStruct {
std::set<std::string> s1;
std::set<std::string> s2;
bool operator==(const myStruct &other) const {
return (s1 == other.s1) && (s2 == other.s2);
}
};
// hash helper functions plagiarized from Boost
template <typename T>
void hash_combine(size_t &seed, const T &v)
{
using std::hash;
seed ^= hash<T>()(v) + 0x9e3779b9 + (seed << 6) + (seed >> 2);
}
template <typename It>
void hash_range(size_t &seed, It first, It last)
{
for (; first != last; ++first) {
hash_combine(seed, *first);
}
}
// std::hash specialization
namespace std
{
template<> struct hash<myStruct> {
size_t operator()(const myStruct &key) const {
size_t seed = 0;
hash_range(seed, key.s1.begin(), key.s1.end());
hash_range(seed, key.s2.begin(), key.s2.end());
return seed;
}
};
}
int main()
{
std::unordered_map<myStruct, int> myMap;
myStruct ms1{ { "apple", "pear", "orange" }, { "red", "green", "blue" } };
myStruct ms2{ { "pear", "apple", "orange" }, { "red", "green", "blue" } };
myStruct ms3{ { "apple", "banana", "orange" }, { "red", "green", "blue" } };
myMap[ms1] = 1;
myMap[ms2] = 2;
myMap[ms3] = 3;
std::cout << myMap.size() << '\n'; // output: 2
}
答案 1 :(得分:1)
std::hash
的要求如下:(http://en.cppreference.com/w/cpp/utility/hash)
哈希模板定义了一个实现哈希函数的函数对象。此函数对象的实例满足Hash。特别是,他们定义了一个 operator():
Key
类型的单个参数。 size_t
的值,表示参数的哈希值。 k1
和k2
std::hash<Key>()(k1) == std::hash<Key>()(k2)
。 k1
和k2
,std::hash<Key>()(k1) == std::hash<Key>()(k2)
应该非常小,接近1.0 / std::numeric_limits<size_t>::max()
的概率。 哈希模板是 CopyConstructible 和可破坏。
所以你需要的基本上是一个返回std::size_t
的函数,它对每个myStruct
对象都是唯一的,并且对于被认为是等价的对象返回相同的值。
一种方法是使用std::hash<std::string>
的标准专门化,方法是使用分隔符序列连接每个std::set
成员中的所有字符串,然后连接所有结果将字符串合并为一个并使用标准哈希函数返回哈希值。
如果成员myStruct
不同,合并的&#34;超级&#34; -string对每个std::set
对象都是唯一的,当成员不同时,它们仍然相同std::set
是一个有序的容器。
struct myStruct {
std::set<std::string> s1;
std::set<std::string> s2;
};
std::string mergeAllStrings(const myStruct& ms) {
static const std::string SEPARATOR = "#¤%&"; // Some uncommon sequence.
std::string super;
for (const auto& s : ms.s1) {
super += s + SEPARATOR; // Append separator.
}
for (const auto& s : ms.s2) {
super += s + SEPARATOR; // Append separator.
}
return super;
}
int main() {
myStruct ms1{{"apple", "pear", "orange"}, {"red", "green", "blue"}};
myStruct ms2{{"pear", "apple", "orange"}, {"red", "green", "blue"}};
myStruct ms3{{"apple", "banana", "orange"}, {"red", "green", "blue"}};
std::cout << std::hash<std::string>()(mergeAllStrings(ms1)) << std::endl;
std::cout << std::hash<std::string>()(mergeAllStrings(ms2)) << std::endl;
std::cout << std::hash<std::string>()(mergeAllStrings(ms3)) << std::endl;
}
输出:
2681724430859750844 // Same
2681724430859750844 // Same
2942368903851914580 // Different
您现在可以创建一个哈希函子,例如为:
struct MyHash {
std::size_t operator()(const myStruct& ms) const {
return std::hash<std::string>()(mergeAllStrings(ms));
}
};
并将其与std::unordered_map
一起用作:
std::unordered_map<myStruct, myValue, MyHash> m;
请注意,您还应该提供自定义equal_to
仿函数。