我想我会尝试通过散列它们来选择不同的选项作为字符串,但这不起作用:
#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
}
我似乎无法在编译时获取哈希值。根据我的阅读,每个不同的输入每次都应该产生相同的唯一输出,并且冲突的可能性非常低。考虑到这些属性,不能在编译时计算哈希值吗?我对散列不太了解,我通常使用 unordered_map,但为了学习我想尝试一些新的东西。
std::hash::operator()
不是 constexpr
,所以你不能只使用它。相反,您必须编写自己的 constexpr
哈希函数。例如,以下是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;
}
然后就可以使用它了:
int selectMenuOptionString(const std::string& str)
{
switch (hashString(str))
{
case hashString(std::string_view("Selection one")): return 42;
default: return 0;
}
}
请注意,如果您编写了
hashString("Selection one")
,它实际上也会对空终止符进行哈希处理,因此您可能需要重载来捕获字符串文字,例如:
template <size_t N>
constexpr size_t hashString(char const (&toHash)[N])
{
return hashString(std::string_view(toHash));
}
您需要实现自己的哈希函数,因为 constexpr 没有合适的 std::hash 实例化。 这是一个便宜又肮脏的...
编辑:为了不被 Justin 的回答羞辱得太严重,我添加了一个 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;
}
我只是想添加这个,因为我觉得它很酷。我从这里的一个问题中得到的 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");
}
这是 Justin 的 答案的一个版本,它也使用 适当的 FNV 哈希参数 处理 32 位(如 Jive Dadson 的 答案所暗示的):
template <typename Str>
constexpr size_t hashString(const Str& toHash)
{
// Based on
static_assert(sizeof(size_t) == 8 || sizeof(size_t) == 4);
// fnv-1
size_t result = []{
if constexpr(sizeof(size_t) == 8) return 0xcbf29ce484222325;
else return 0x811c9dc5;
}();
const size_t prime = []{
if constexpr(sizeof(size_t) == 8) return 0x00000100000001b3;
else return 0x01000193;
}();
for (char c : toHash) {
result *= prime;
result ^= c;
}
return result;
}
值得注意的是,如果您需要不区分大小写的基本版本,只需将 XOR 行更改为:
result ^= std::tolower(c);
您可能需要
#include <cctype>
。此版本将受到 std::tolower
不能很好地处理非 ASCII 编码的所有限制。
你无法在编译时获取运行时值的哈希值,不。
即使您传递了
std::hash
一个常量表达式,它也未被定义为能够在编译时执行其哈希工作。
据我所知(这并不远),你必须想出一些可怕的模板元黑客(或者更糟糕的是宏!)才能做到这一点。就我个人而言,如果您的文本输入在构建时已知,我只需在代码外部预生成一个哈希值,也许是在某些 Python 驱动的预构建步骤中。