我正在研究new hashing system。部分实现需要将指向某个内存位置的指针传递给具有如下签名的算法仿函数:
void
operator()(void const* key, std::size_t len) noexcept
{
unsigned char const* p = static_cast<unsigned char const*>(key);
unsigned char const* const e = p + len;
std::size_t h = 14695981039346656037u;
for (; p < e; ++p)
h = (h ^ *p) * 1099511628211u;
return h;
}
当对基本类型进行操作时,我只是传入一个指向类型开头和大小的指针:
template <class HASHALG>
void hash_append(HASHALG& hashAlg, char const input)
{
hashAlg(&input, sizeof(input));
}
我问是否有关于布尔值的二进制表示的保证,是因为我想知道以下是否会按预期运行:
template <class HASHALG>
void hash_append(HASHALG& hashAlg, bool const input)
{
hashAlg(&input, sizeof(input));
}
我担心的可能是编译器可能会选择真正的bool可以有任何非零积分表示。即:
10110010 => true
10101010 => true
10100010 => true
00100010 => true
01100110 => true
00000000 => false
如果是这种情况,那么散列为字节无效,因为相同的值(true
)可以产生许多不同的散列。
我搜索了标准,我能找到的是以下两个部分:
(3.9.1.7)类型bool,char,char16_t,char32_t,wchar_t以及有符号和无符号整数类型统称为整数类型。整数类型的同义词是整数类型。整数类型的表示应使用纯二进制计算系统定义值。
(4.5.6)bool类型的prvalue可以转换为int类型的prvalue,false变为零,true变为1。
所以我知道一个int将有一个整数表示,我知道当转换为int时,它将是1或0,但标准是否保证它将具有固定的表示?在大多数情况下,编译器似乎只是实现了这个:
true => 00000001
false => 00000000
如果无法保证这将成为代表,我不想被一些不起眼的边缘案件所焚烧。
答案 0 :(得分:1)
除了char
之外的所有类型都可以有填充位(也就是非值位)
struct
通常甚至都有整个填充字节
此外,某些类型具有相同值的多个表示,并且一些具有陷阱表示。
对于大多数浮点数,有许多NaN和两个零 在分段体系结构中,具有不同表示的指针可能比较相等。
大多数实现将bool
限制为每个值的一个表示,这有利有弊。 (见过a
和!a
都是假/真?)
因此,您的哈希方法可能不合适......
也许对受影响的原始类型进行预转换?
并明确传递struct
的所有成员?
答案 1 :(得分:0)
没有保证。你没有显示使用void指针键的位置,但我觉得你会使用一个具有未定义行为的强制转换。
答案 2 :(得分:0)
真正的问题是hash(x)==hash(y)
应该适用于任何x==y
,无论其类型如何。我希望浮点数的问题更大,因为+0.0和-0.0是相等但有不同的表示。
对于bool
,至少可以通过专门化hashAlg<bool>(bool b)
来返回int(b)
来修复代表问题。实际上,您可能希望对所有小类型(也是char和short)执行相同的操作。使用给定的公式对它们进行哈希处理只会产生意外的碰撞。