编译char类型的时间一致哈希

时间:2012-06-22 08:11:24

标签: c++ hash c++11 user-defined-literals

我想以类似于ruby的方式实现Symbol

为此,我创建了一个用户定义的文字,该文字返回std::hash对应的std::basic_string<T>

代码很棒,但是当我阅读somewhere时,哈希函数可能在同一程序的多次执行中不一致。此外,我想在编译时进行此计算,1)std::hash不支持,2)如果std::hash返回值更改,则会破坏代码。

所以我根据java.lang.String.hashCode实现编写了以下实现。

typedef size_t symbol;

template<typename CharT>
constexpr size_t constant_hash(const CharT* p, size_t h = 0) noexcept
{
    return (*p == 0) ? h : constant_hash(p + 1, h * 31 + static_cast<size_t>(*p));
}

constexpr symbol operator "" _sym (const char* p, size_t n) noexcept
{
    return constant_hash(p);
}

我的问题是:这个实现有什么问题吗?

我只能在GCC 4.7.1上进行测试,我想知道它是否符合标准, 也应该适用于其他编译器。

我问这个是因为之前的实现正在GCC上工作,但是如果二进制文件是用clang ++编译的话会引起段错误(我认为增量运算符存在未定义行为的问题)。

提前致谢

修改

使用clang ++(感谢KennyTM)

3 个答案:

答案 0 :(得分:2)

没有UB,只要字符串终止'\0',它就可以正常工作。请注意constexpr评估不能调用UB;在运行时导致UB的算术或指针操作需要在常量表达式的上下文中产生编译错误。

请注意,static_cast是不必要的; char操作数将被提升为size_t

此外,乍一看哈希函数看起来不太好,因为h * 31只是( h << 5 ) - h。你可以选择一个更大的数字,1在整个二进制值中随机分布。但另一方面,他们可能会试图变得聪明,因为ASCII的低5位具有最大的熵,这消除了不同长度的短字符串之间发生冲突的可能性。

答案 1 :(得分:2)

注意:n3333是C ++ 17的提案。虽然我不相信C ++ 11要求哈希在多次运行中产生相同的结果,但实际上,我相信所有当前的实现都会这样做。

答案 2 :(得分:1)

在当前的活动C ++标准中,散列函数的定义通常以这样的方式编写,以允许特定于域的实现的更多可能性,而不是要求以特定方式执行散列。例如,它允许执行池化的字符串并使用池实例的内存位置作为哈希值(顺便说一句,这就是ruby的字符串和哈希值,这导致了一些interesting issues) 。如果你在数据而不是引用上计算你的哈希值,那么值将是稳定的 - 除非你发现某种形式的数学,其中常量表达式不是。

基本上,在这种情况下,“可能不会”是授予权限以使事物以特定方式行事,而不是陈述可能发生的事情。

也就是说,如果你使用std::hash,你不能保证执行之间的值总是相同的(并且将来,如果采用n3333,任何依赖于的代码都会中断),因此如果需要稳定的散列,最好定义自己的稳定散列函数。