我想使用由 boost's 实现的缓存,unordered_map
从 adynamic_bitset
到 a dynamic_bitset
。当然,问题在于 bitset 中没有默认的散列函数。这似乎不是一个概念问题,但我不知道如何解决技术问题。我该怎么做?
5 回答
我找到了一个意想不到的解决方案。事实证明,boost 可以选择#define BOOST_DYNAMIC_BITSET_DONT_USE_FRIENDS
. 定义后,包括私有成员在内的私有成员将m_bits
变为公共成员(我认为它可以处理旧的编译器或其他东西)。
所以现在我可以使用@KennyTM 的答案,稍微改变一下:
namespace boost {
template <typename B, typename A>
std::size_t hash_value(const boost::dynamic_bitset<B, A>& bs) {
return boost::hash_value(bs.m_bits);
}
}
有一个to_block_range
功能可以将 bitset 包含的单词复制到某个缓冲区中。为避免实际复制,您可以定义自己的“输出迭代器”,它只处理单个单词并从中计算哈希值。关于。如何计算哈希:参见例如 FNV 哈希函数。
不幸的是,dynamic_bitset
恕我直言,它的设计是脑残,因为它不能让您直接访问底层缓冲区(甚至不作为const
)。
这是一个功能请求。
可以通过将位集转换为临时向量来实现效率不高的唯一哈希:
namespace boost {
template <typename B, typename A>
std::size_t hash_value(const boost::dynamic_bitset<B, A>& bs) {
std::vector<B, A> v;
boost::to_block_range(bs, std::back_inserter(v));
return boost::hash_value(v);
}
}
我们不能直接计算哈希,因为里面的底层数据dynamic_bitset
是私有的(m_bits
)
但是我们可以很容易地绕过(颠覆!)c++ 访问规范系统,而无需任何一个
- 破解代码或
- 假装你的编译器不合格(
BOOST_DYNAMIC_BITSET_DONT_USE_FRIENDS
)
关键是模板函数to_block_range
,它是一个friend
to dynamic_bitset
。因此,此功能的专业化也可以访问其私有数据(即m_bits
)。
生成的代码再简单不过了
namespace boost {
// specialise dynamic bitset for size_t& to return the hash of the underlying data
template <>
inline void
to_block_range(const dynamic_bitset<>& b, size_t& hash_result)
{
hash_result = boost::hash_value(bs.m_bits);
}
std::size_t hash_value(const boost::dynamic_bitset<B, A>& bs)
{
size_t hash_result;
to_block_range(bs, hash_result);
return hash_result;
}
}
建议的解决方案在以下情况下生成相同的哈希。
#define BOOST_DYNAMIC_BITSET_DONT_USE_FRIENDS
namespace boost {
template <typename B, typename A>
std::size_t hash_value(const boost::dynamic_bitset<B, A>& bs) {
return boost::hash_value(bs.m_bits);
}
}
boost::dynamic_biset<> test(1,false);
auto hash1 = boost::hash_value(test);
test.push_back(false);
auto hash2 = boost::hash_value(test);
// keep continue...
test.push_back(false);
auto hash31 = boost::hash_value(test);
// magically all hash1 to hash31 are the same!
建议的解决方案有时不适用于哈希映射。
我阅读了 dynamic_bitset 的源代码为什么会发生这种情况,并意识到 dynamic_bitset 每个值存储一位与vector<bool>
. 例如,您调用dynamic_bitset<> test(1, false)
,然后 dynamic_bitset 最初分配 4 个全为零的字节,它保存位的大小(在这种情况下,大小为 1)。请注意,如果位的大小大于 32,则它会再次分配 4 个字节并将其推回dynamic_bitsets<>::m_bits
(因此 m_bits 是 4 个字节块的向量)。
如果我调用test.push_back(x)
,它会将第二位设置为 x 并将位的大小增加到 2。如果x
为假,则m_bits[0]
根本不会改变!为了正确计算哈希,我们需要在哈希计算中使用 m_num_bits。
那么,问题是如何?
1:使用boost::hash_combine
这种方法简单直接。我没有检查这个编译与否。
namespace boost {
template <typename B, typename A>
std::size_t hash_value(const boost::dynamic_bitset<B, A>& bs) {
size_t tmp = 0;
boost::hash_combine(tmp,bs.m_num_bits);
boost::hash_combine(tmp,bs.m_bits);
return tmp;
}
}
2:翻转 m_num_bits % bits_per_block th 位。根据位大小翻转位。我相信这种方法比 1 更快。
namespace boost {
template <typename B, typename A>
std::size_t hash_value(const boost::dynamic_bitset<B, A>& bs) {
// you may need more sophisticated bit shift approach.
auto bit = 1u << (bs.m_num_bits % bs.bits_per_block);
auto return_val = boost::hash_value(bs.m_bits);
// sorry this was wrong
//return (return_val & bit) ? return_val | bit : return_val & (~bit);
return (return_val & bit) ? return_val & (~bit) : return_val | bit;
}
}