C++如何将string转换为enum枚举类型用于switch语句?
首先得纠正一个常见的认知偏差:你朋友说switch比if-else快,前提是switch判断的是编译期就能确定的整数、枚举值,靠跳转表实现O(1)分支。你现在为了用上switch,先跑一遍std::map的字符串查找——这玩意是红黑树实现的,每次查找O(logn)开销,这点开销早就把switch那点性能优势磨没了,纯粹是为了凑语法硬加中间层,完全没必要。
哈希值实现switch的可行性
网上说的用哈希值实现switch确实能跑,原理是自己写个编译期可计算的哈希函数,把字符串转成整数再喂给switch,省了map查找的步骤,看起来和原生switch一样简洁,示例代码如下:
// 简易constexpr字符串哈希 constexpr unsigned int hash_str(const char* str, int h = 0) { return !str[h] ? 5381 : (hash_str(str, h+1) * 33) ^ str[h]; } void test::processInput(vector<string> input) { switch (hash_str(input[0].c_str())) { case hash_str("min"): MIN(input); break; case hash_str("max"): MAX(input); break; case hash_str("avg"): AVG(input); break; case hash_str("count"): COUNT(input); break; case hash_str("stdev"): STDEV(input); break; case hash_str("sum"): SUM(input); break; case hash_str("var"): VAR(input); break; case hash_str("pow"): POW(input); break; default: std::cout << "Error, input not found" << std::endl; } }
但这方案有个躲不开的坑:哈希碰撞风险。哪怕你选的哈希算法碰撞概率再低,只要存在两个不同命令串算出同一个哈希值的可能,线上跑的时候就有概率出莫名其妙的串命令bug,这种小概率问题查起来成本极高,生产环境不推荐使用。
免手动枚举映射的最优方案
你嫌手动写枚举和字符串的一一映射麻烦,其实完全可以把枚举这层中间商砍掉,直接做字符串到处理函数的分发表,这是这类命令处理场景最通用的写法,代码量比你现在的实现少一半,维护也更方便:
// 头文件中定义分发表,无需额外定义枚举 #include <unordered_map> #include <functional> #include <string_view> class test { private: inline static const std::unordered_map<std::string_view, std::function<void(std::vector<std::string>&)>> cmdMap = { {"min", &test::MIN}, {"max", &test::MAX}, {"avg", &test::AVG}, {"count", &test::COUNT}, {"stdev", &test::STDEV}, {"sum", &test::SUM}, {"var", &test::VAR}, {"pow", &test::POW} }; public: void processInput(std::vector<std::string> input); // 其余MIN/MAX等处理函数声明保持原有逻辑即可 };
对应cpp中的实现可以简化成:
void test::processInput(std::vector<std::string> input) { auto it = cmdMap.find(input[0]); if (it != cmdMap.end()) { it->second(input); } else { std::cout << "Error, input not found" << std::endl; } }
这个方案的优势很明显:
- 不需要额外定义枚举层,不需要做两次映射,新增命令只需要在
cmdMap里加一行条目即可 - 用
std::unordered_map哈希表实现,查找效率是O(1),比你之前用的std::map更快 - 没有哈希碰撞风险,逻辑稳定性高
- 代码可读性不输switch,看到映射条目就能直接对应到处理函数
顺便提一句你原有代码的bug:用map的下标访问inMap[input[0]]时,如果输入的命令不存在,会自动在map里插入一个对应键的默认枚举值,根本不会走到你写的default分支,会触发很隐蔽的逻辑错误,不管用哪种map,查找不确定是否存在的键都要用find方法,不要用下标访问。
要不要退回if-else实现?
完全看你的业务规模:如果总共就七八个命令,后续也很少新增,那if-else写着最省心,这个命令量级下的性能差异用户根本感知不到,怎么顺手怎么来。如果后续会持续新增命令,还是推荐上面的分发表方案,维护成本低很多。
内容的提问来源于stack exchange,提问作者noClueAboutWhatIMDoing

