gpt4 book ai didi

c++ - std::hash 值在 char* 值上而不是在内存地址上?

转载 作者:可可西里 更新时间:2023-11-01 18:18:25 36 4
gpt4 key购买 nike

如本 link 所述:

There is no specialization for C strings. std::hash produces a hash of the value of the pointer (the memory address), it does not examine the contents of any character array.

这意味着使用相同的 char* 值,可以产生不同的哈希码。例如,有这样的代码:

//MOK and MOV are template arguments
void emit(MOK key, MOV value) {
auto h = hash<MOK>()(key);
cout<<"key="<<key<<" h="<<h<<endl;
...

这是通过调用 4 次 emit() 对相同的 key(使用 MOK=char*)值(但是4 个不同的标记/字符串对象):

key=hello h=140311481289184
key=hello h=140311414180320
key=hello h=140311414180326
key=hello h=140311481289190

如何为 char* 获取相同的哈希码?我不想使用 boost

最佳答案

当然有创建临时 std::string 并对其进行散列的简单(且缓慢)解决方案。如果您不想这样做,恐怕您将不得不实现自己的哈希函数。遗憾的是,当前的 C++ 标准库不提供与特定于对象的哈希解决方案分离的通用哈希算法。 (但有 some hope 这可能会在未来改变。)

假设你有一个函数

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);
}
};

然后针对您感兴趣的类型专门化它。

template <>
struct myhash<std::string>
{
std::size_t
operator()(const std::string& s) const noexcept
{
return hash_bytes(s.data(), s.size());
}
};

template <>
struct myhash<const char *>
{
std::size_t
operator()(const char *const s) const noexcept
{
return hash_bytes(s, std::strlen(s));
}
};

这让您只剩下实现 hash_bytes 的练习。幸运的是,有一些相当不错的散列函数也很容易实现。我的简单散列算法是 Fowler-Noll-Vo hash function .您可以用五行代码实现它;请参阅链接的维基百科文章。

如果你想变得有点花哨,请考虑以下实现。首先,我定义了一个通用模板,它可以专用于任何版本的 FNV-1a 哈希函数。

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_;
}

};

接下来,我为 32 位和 64 位版本提供别名。参数取自 Landon Curt Noll's website .

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();
}

请注意此代码如何自动适应 std::size_t 为 32 或 64 位宽的平台。

关于c++ - std::hash 值在 char* 值上而不是在内存地址上?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/34597260/

36 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com