C++ std::char*值上的哈希值,而不是内存地址上的哈希值?
如本文所述: C字符串没有专门化。哈希生成指针值(内存地址)的哈希,它不检查任何字符数组的内容 这意味着使用相同的C++ std::char*值上的哈希值,而不是内存地址上的哈希值?,c++,c++11,hash,C++,C++11,Hash,如本文所述: C字符串没有专门化。哈希生成指针值(内存地址)的哈希,它不检查任何字符数组的内容 这意味着使用相同的char*值,可以生成不同的哈希代码。例如,拥有以下代码: //MOK and MOV are template arguments void emit(MOK key, MOV value) { auto h = hash<MOK>()(key); cout<<"key="<<key<<" h="<<h&l
char*
值,可以生成不同的哈希代码。例如,拥有以下代码:
//MOK and MOV are template arguments
void emit(MOK key, MOV value) {
auto h = hash<MOK>()(key);
cout<<"key="<<key<<" h="<<h<<endl;
...
如何为
char*
获取相同的哈希代码?我不想使用boost
我以前不得不这样做,最后编写了一个函数来实现这一点,基本上与以下实现相同:
size\u t hash\u c\u字符串(const char*p,size\t s){
大小\u t结果=0;
常量大小\u t素数=31;
对于(尺寸i=0;i
请注意,这不是一个加密安全的散列,但它足够快,并产生良好的结果。当然有一个简单(缓慢)的解决方案,即创建一个临时的
std::string
并对其进行散列。如果您不想这样做,恐怕您必须实现自己的哈希函数。可悲的是,当前C++标准库不提供从特定于对象的哈希解决方案中解开的通用哈希算法。(但这在未来可能会发生变化。)
假设你有一个函数
std::size_t
hash_bytes(const void * data, std::size_t size) noexcept;
这将获取一个地址和一个大小,并返回一个哈希值,该哈希值是根据地址后面的那个么多字节计算出来的。在这个函数的帮助下,您可以轻松地编写
template <typename T>
struct myhash
{
std::size_t
operator()(const T& obj) const noexcept
{
// Fallback implementation.
auto hashfn = std::hash<T> {};
return hashfn(obj);
}
};
接下来,我将为32位和64位版本提供别名。参数取自
使用fnv1a\u 32=basic\u fnv1a;
使用fnv1a_64=基本fnv1a;
最后,我提供类型元函数来选择给定所需位数的算法版本
template <std::size_t Bits>
struct fnv1a;
template <>
struct fnv1a<32>
{
using type = fnv1a_32;
};
template <>
struct fnv1a<64>
{
using type = fnv1a_64;
};
template <std::size_t Bits>
using fnv1a_t = typename fnv1a<Bits>::type;
模板
结构fnv1a;
模板
结构fnv1a
{
使用类型=fnv1a_32;
};
模板
结构fnv1a
{
使用类型=fnv1a_64;
};
模板
使用fnv1a\u t=typename fnv1a::type;
有了这些,我们就可以出发了
constexpr std::size_t
hash_bytes(const void *const data, const std::size_t size) noexcept
{
auto hashfn = fnv1a_t<CHAR_BIT * sizeof(std::size_t)> {};
hashfn.update(data, size);
return hashfn.digest();
}
constexpr std::size\u t
哈希字节(常量无效*常量数据,常量标准::大小大小)无异常
{
auto hashfn=fnv1a_t{};
更新(数据、大小);
返回hashfn.digest();
}
请注意此代码如何自动适应
std::size\t
为32或64位宽的平台。在C++17中,您应该使用std::hash
,它可以无缝工作,因为const char*
可以隐式转换为它。自从C++17添加了std::hash
专门化后,您可以使用它它用于计算C字符串的哈希值
例如:
#include <string_view>
#include <cstring>
static size_t hash_cstr(const char *s)
{
return std::hash<std::string_view>()(std::string_view(s, std::strlen(s)));
}
那么,您的目标是将
“hello,world”
和std::string{“hello,world”}
哈希值设置为相同的值吗?对std::string
有专门化,但对C字符串没有专门化。你能用std::string
显示得到这个结果的实际代码吗?你不能用std::string
s吗?你为什么不专门化std::hash
?@vsoftco,因为它违反了中的全面禁止。为什么不直接专门化std::hash呢?它不应该是一个例外IIRC。而且在将来,我们可能能够做std::hash(std::string\u view(“some\u key”))
,这应该很便宜。@BillLynch我也在想,这会很酷。但我还记得有人在争论std::string_view
上的等价性应该用指针地址来定义还是指向内存。如果是前者,我们就不能用它进行散列。你知道当前状态是什么吗?@5gon12eder:cppreference说如果string\u view(“abc”)==string\u view(“abc”)
那么hash(string\u view(“abc”)==hash(string\u view(“abc”)
。所以,除非他们决定做一个无用的相等运算符,否则我们应该没事。这是一个糟糕的答案。谢谢。const char*s=“foo”h1=std::hash{}(std::string_view(s,std::strlen(s)))std::string s2(“foo”)h2=std::hash{}(s2)将s1==s2?@sandwood我看不出你在哪里定义s1。你是不是想问h1==h2?C++标准不要求从实例中代码<> H1和template <typename ResultT, ResultT OffsetBasis, ResultT Prime>
class basic_fnv1a final
{
static_assert(std::is_unsigned<ResultT>::value, "need unsigned integer");
public:
using result_type = ResultT;
private:
result_type state_ {};
public:
constexpr
basic_fnv1a() noexcept : state_ {OffsetBasis}
{
}
constexpr void
update(const void *const data, const std::size_t size) noexcept
{
const auto cdata = static_cast<const unsigned char *>(data);
auto acc = this->state_;
for (auto i = std::size_t {}; i < size; ++i)
{
const auto next = std::size_t {cdata[i]};
acc = (acc ^ next) * Prime;
}
this->state_ = acc;
}
constexpr result_type
digest() const noexcept
{
return this->state_;
}
};
using fnv1a_32 = basic_fnv1a<std::uint32_t,
UINT32_C(2166136261),
UINT32_C(16777619)>;
using fnv1a_64 = basic_fnv1a<std::uint64_t,
UINT64_C(14695981039346656037),
UINT64_C(1099511628211)>;
template <std::size_t Bits>
struct fnv1a;
template <>
struct fnv1a<32>
{
using type = fnv1a_32;
};
template <>
struct fnv1a<64>
{
using type = fnv1a_64;
};
template <std::size_t Bits>
using fnv1a_t = typename fnv1a<Bits>::type;
constexpr std::size_t
hash_bytes(const void *const data, const std::size_t size) noexcept
{
auto hashfn = fnv1a_t<CHAR_BIT * sizeof(std::size_t)> {};
hashfn.update(data, size);
return hashfn.digest();
}
#include <string_view>
#include <cstring>
static size_t hash_cstr(const char *s)
{
return std::hash<std::string_view>()(std::string_view(s, std::strlen(s)));
}
#include <functional>
// -> which finally includes /usr/include/c++/$x/bits/hash_bytes.h
#include <cstring>
static size_t hash_cstr_gnu(const char *s)
{
const size_t seed = 0;
return std::_Hash_bytes(s, std::strlen(s), seed);
}