【问题标题】:How to convert a string to a constant integer?如何将字符串转换为常量整数?
【发布时间】:2017-04-04 10:16:59
【问题描述】:

我有代表图像名称的字符串,例如“foobar.png”等。 如您所知,C++ 中的 switch-case 不支持切换字符串。

我正在尝试解决这个问题,方法是将字符串散列到 std::size_t,然后在 switch-case 语句中使用该值。

例如:

        //frameName is an std::string which represents foobar.png etc..
        switch (shs(frameName)) { //shs is my hash func which returns std::size_t;
            case shs(Pfn::fs1x1): //Problem in this line
            default:{
                break;
            }
        }

在单独的文件 (Pfn.hpp) 中:

命名空间 Pfn{ 常量 std::string fs1x1 = "fs1x1"; };

问题是,在我的 case 语句中,编译器报告 shs(Pfn::fs1x1) 不是常量表达式。确切的错误信息是:

case 值不是常量表达式:

提前计算出所有哈希值然后将它们硬编码到 case 语句中会非常乏味。您对我如何在运行时以某种方式创建常量表达式有什么建议吗?

编辑:我的 shs 函数:

static std::size_t shs(std::string string){
    return Hash::shs::hs(string);
}

//...

namespace Hash{
    struct shs{
    public:
        inline std::size_t operator()(const std::string &string)const{
            return hashString(string);
        }

        static std::size_t hs(const std::string &string){
            std::size_t seed = 0;
            hash_combine(seed,string);
            return seed;
        }

        //From Boost::hash_combine.
        template <class T>
        static inline void hash_combine(std::size_t& seed, const T& v)
        {
            std::hash<T> hasher;
            seed ^= hasher(v) + 0x9e3779b9 + (seed<<6) + (seed>>2);
        };
    };
}

【问题讨论】:

  • 你能添加你的函数shs吗?

标签: c++ c++11


【解决方案1】:

shs 的参数必须是constexpr 并且shs 本身也必须是constexpr。由于 C++11 对 constexpr 函数的限制,您可能希望为哈希的编译时版本和运行时版本提供不同的实现。

wrote 前段时间就这个主题发表过一篇文章,使用fnv1a 作为哈希算法。以下是重要部分:

常量:

typedef std::uint64_t hash_t;

constexpr hash_t prime = 0x100000001B3ull;
constexpr hash_t basis = 0xCBF29CE484222325ull;

运行时哈希:

hash_t hash(char const* str)
{
    hash_t ret{basis};

    while(*str){
        ret ^= *str;
        ret *= prime;
        str++;
    }

    return ret;
}

编译时哈希:

constexpr hash_t hash_compile_time(char const* str, hash_t last_value = basis)
{
    return *str ? hash_compile_time(str+1, (*str ^ last_value) * prime) : last_value;
}

用户定义的字符串字面量:

constexpr unsigned long long operator "" _hash(char const* p, size_t)
{
    return hash_compile_time(p);
}

及用法:

switch(fnv1a_64::hash(str)){
case "first"_hash:
    cout << "1st one" << endl;
    break;
case "second"_hash:
    cout << "2nd one" << endl;
    break;
case "third"_hash:
    cout << "3rd one" << endl;
    break;
default:
    cout << "Default..." << endl;
}

demo

但是,请想想孩子们!除非您可以保证不会发生哈希冲突,否则这是在玩火,不适合作为生产代码。

【讨论】:

  • 嗯..说实话,我的应用程序的图像资产还没有准备好,它打断了我的“流程”,不得不自己创建图像以在应用程序中使用,所以我只是在生产图像准备好之前,使用 switch case 快速生成图像。你能解释一下你的散列函数是如何工作的吗?以及您的“”运算符重载?它很有趣......我不知道你能做到这一点......
  • 我想如果我正在使用一组有限的字符串,我可以在所有字符串上调用这个函数并检查冲突,因为它应该是确定性的并且在所有字符串上都返回相同的值平台...“素数”在哪里定义...我可以使用任何素数?
  • @John 好吧,就哈希而言,我只是实现了 fnv1a(链接)。用户定义的文字是 C++11 中的新增内容,正是针对这种情况。如您所见,它本身就是constexpr,并将 constexpr 数据传递给能够在编译时生成散列的函数。我不确定对此还能说什么,但请走开。我会在一秒钟内用质数更新帖子,我不是 100% 确定,但有些质数可能比其他质数更好,我使用了算法创建者提出的一个。
  • @John 如果您的输入始终是其中一种情况,那么就没有冲突的风险 - 如果情况本身发生冲突,您会收到编译时错误,因此使用是安全的。
  • 这实际上是不可思议的......所以我想我什至不需要测试哈希函数中是否存在冲突!
【解决方案2】:

出于此答案的目的,我假设您的哈希不会有任何冲突。

如果您将散列定义为预处理器函数,则可以创建可以匹配的常量。

这篇文章可能会有所帮助:Compile-time (preprocessor) hashing of string

【讨论】:

    【解决方案3】:

    根据定义,常量表达式是指可以在编译时计算的表达式。

    但是如果func()constexpr,你可以使用case func():

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-10-03
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2010-10-13
      • 1970-01-01
      相关资源
      最近更新 更多