我想以类似于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)
答案 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,任何依赖于的代码都会中断),因此如果需要稳定的散列,最好定义自己的稳定散列函数。