简体   繁体   English

是否可以将哈希值作为编译时常量?

[英]Is it possible to get hash values as compile-time constants?

I thought I'd try selecting different options as strings by hashing them, but this doesn't work: 我以为我会尝试通过散列来选择不同的选项作为字符串,但这不起作用:

#include <type_traits>
#include <string>

inline void selectMenuOptionString(const std::string& str)
{
    switch (std::hash<std::string>()(str))
    {
    case std::hash<std::string>()(std::string("Selection one")) : break; 
        // Expression must have a constant value
    }
}

inline void selectMenuOptionString2(const std::string& str)
{
    size_t selectionOneHash = std::hash<std::string>()(std::string("Selection one"));

    switch (std::hash<std::string>()(str))
    {
    case selectionOneHash: // Expression must have a constant value 
                           // The variable of selectionOneHash cannot be used as a constant
    }

    constexpr size_t hash = std::hash<int>()(6); // Expression must have a constant value
}

It seems I can't get hash values at compile time. 似乎我无法在编译时获取哈希值。 From what I've read each different input should yield the same unique output every time, with a very low chance of collision. 根据我的阅读,每次不同的输入应该每次产生相同的唯一输出,碰撞的可能性非常低。 Given these properties couldn't the hash value be calculated at compile time? 鉴于这些属性无法在编译时计算哈希值? I don't know much at all about hashing, I usually use an unordered_map, but I wanted to try something new for learning's sake. 我对哈希很不了解,我通常使用unordered_map,但我想尝试新的东西以便学习。

std::hash::operator() isn't constexpr , so you can't just use it. std::hash::operator()不是constexpr ,所以你不能只使用它。 Instead, you'd have to write your own constexpr hash function. 相反,你必须编写自己的constexpr哈希函数。 For example, the following is the FNV-1a hash algorithm (untested): 例如,以下是FNV-1a哈希算法 (未经测试):

template <typename Str>
constexpr size_t hashString(const Str& toHash)
{
    // For this example, I'm requiring size_t to be 64-bit, but you could
    // easily change the offset and prime used to the appropriate ones
    // based on sizeof(size_t).
    static_assert(sizeof(size_t) == 8);
    // FNV-1a 64 bit algorithm
    size_t result = 0xcbf29ce484222325; // FNV offset basis

    for (char c : toHash) {
        result ^= c;
        result *= 1099511628211; // FNV prime
    }

    return result;
}

And then you can use it: 然后你可以使用它:

int selectMenuOptionString(const std::string& str)
{
    switch (hashString(str))
    {
    case hashString(std::string_view("Selection one")): return 42; 
    default: return 0;
    }
}

Note that if you wrote hashString("Selection one") , it would actually hash the null terminator as well, so you might want to have an overload to catch string literals, such as: 请注意,如果您编写了hashString("Selection one") ,它实际上也会对null终止符进行哈希处理,因此您可能希望有一个重载来捕获字符串文字,例如:

template <size_t N>
constexpr size_t hashString(char const (&toHash)[N])
{
    return hashString(std::string_view(toHash));
}

Demo 演示

You'll need to implement your own hash function, because there's no suitable instantiation of std::hash that's constexpr. 你需要实现自己的哈希函数,因为没有合适的std :: hash实例化的constexpr。 Here's a cheap-and-dirty... 这是一个便宜又脏的......

EDIT: In order not to be humiliated too badly by Justin's answer, I added a 32 bit branch. 编辑:为了不被贾斯汀的回答羞辱太多,我添加了一个32位分支。

    constexpr size_t hash(const char *str) {
    static_assert(sizeof(size_t) == 8 || sizeof(size_t) == 4);
    size_t h = 0;
    if constexpr(sizeof(size_t) == 8) {
        h = 1125899906842597L; // prime
    } else {
        h = 4294967291L;
    }
    int i = 0;
    while (str[i] != 0) {
        h = 31 * h + str[i++];
    }

    return h;
}

You can't get the hash of a runtime value at compile-time, no. 您无法在编译时获取运行时值的哈希值,不可以。

Even if you passed std::hash a constant expression, it is not defined to be able to do its hashing work at compile-time. 即使你传递了std::hash一个常量表达式,它也没有定义为能够在编译时进行散列工作。

As far as I know (which isn't far), you'd have to come up with some monstrous template metahackery (or, worse, macros!) to do this. 据我所知(这不远),你必须想出一些怪异的模板metahackery(或更糟糕的是,宏!)来做到这一点。 Personally, if your text input is known at build, I'd just pregenerate a hash outside of the code, perhaps in some Python-driven pre-build step. 就个人而言,如果您的文本输入在构建时已知,我只是在代码之外预生成哈希,可能是在一些Python驱动的预构建步骤中。

I just wanted to add this because I think it's cool. 我只想添加它,因为我觉得它很酷。 The constexpr strlen I got from a question here: constexpr strlen 我在这里提出了一个问题: constexpr strlen

#include <iostream>
#include <string>

int constexpr strlength(const char* str)
{
    return *str ? 1 + strlength(str + 1) : 0;
}

size_t constexpr Hash(const char *first)
{   // FNV-1a hash function 
    const size_t FNVoffsetBasis = 14695981039346656037ULL;
    const size_t FNVprime = 1099511628211ULL;
    const size_t count = strlength(first);
    size_t val = FNVoffsetBasis;
    for (size_t next = 0; next < count; ++next)
    {
        val ^= (size_t)first[next];
        val *= FNVprime;
    }
    return val;
}

inline void selectMenuOptionString(const std::string& str)
{
    switch (Hash(str.c_str()))
    {
    case Hash("Selection one"): /*Do something*/ break;
    case Hash("Selection two"): /*Do something*/ break;
    }
}

int main()
{
    static_assert(strlength("Hello") == 5, "String length not equal");
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM