如何针对constexpr值执行n+m次检查而非n*m次检查?
如何针对constexpr值执行n+m次检查而非n*m次检查?
兄弟,我太懂你这种搞低延迟系统的痛点了——为了榨干性能用编译期模板参数做优化,结果遇到运行时要解析枚举值的情况,要是傻乎乎写全所有n*m种组合的判断,不仅代码冗余到爆炸,还多做了好多没必要的检查,完全违背低延迟的初衷!
先把你的场景补个完整的可复现例子,方便大家理解:
#include <iostream> #include <stdexcept> enum class Color { Red, Green, Blue }; enum class Type { Car, Bike }; // 核心低延迟逻辑,完全依赖编译期模板参数来榨干性能 template<Color C, Type T> void process() { std::cout << "处理 Color(" << static_cast<int>(C) << ") + Type(" << static_cast<int>(T) << ")\n"; } // 反面教材:n*m次检查(3*2=6次),冗余又低效 void bad_dispatch(int color_val, int type_val) { if (color_val == static_cast<int>(Color::Red)) { if (type_val == static_cast<int>(Type::Car)) process<Color::Red, Type::Car>(); else if (type_val == static_cast<int>(Type::Bike)) process<Color::Red, Type::Bike>(); else throw std::invalid_argument("无效的Type值"); } else if (color_val == static_cast<int>(Color::Green)) { if (type_val == static_cast<int>(Type::Car)) process<Color::Green, Type::Car>(); else if (type_val == static_cast<int>(Type::Bike)) process<Color::Green, Type::Bike>(); else throw std::invalid_argument("无效的Type值"); } else if (color_val == static_cast<int>(Color::Blue)) { if (type_val == static_cast<int>(Type::Car)) process<Color::Blue, Type::Car>(); else if (type_val == static_cast<int>(Type::Bike)) process<Color::Blue, Type::Bike>(); else throw std::invalid_argument("无效的Type值"); } else throw std::invalid_argument("无效的Color值"); }
你看,上面的写法不仅要写6个判断分支,要是以后加个Color::Yellow或者Type::Truck,那要改的地方翻一倍,完全不太行。
解决方案:分阶段单分发,把检查次数降到n+m
核心思路就是先把第一个枚举的运行时值解析成编译期模板参数,再在这个编译期分支里解析第二个枚举,这样总检查次数就是「Color的数量 + Type的数量」,而不是两者的乘积。
第一步:写第一个枚举的分发器
先做Color的运行时到编译期的转换,只需要n次检查:
// 专门处理Color的分发,把运行时值绑定到编译期模板参数 template<typename Callback> void dispatch_color(int color_val, Callback&& callback) { if (color_val == static_cast<int>(Color::Red)) { callback.template operator()<Color::Red>(); } else if (color_val == static_cast<int>(Color::Green)) { callback.template operator()<Color::Green>(); } else if (color_val == static_cast<int>(Color::Blue)) { callback.template operator()<Color::Blue>(); } else { throw std::invalid_argument("无效的Color值"); } }
第二步:在Color分支里解析Type
现在在Color的编译期分支里,只需要做m次Type的检查,总共就是n+m次:
// 高效的分发逻辑,总检查次数3+2=5次 void good_dispatch(int color_val, int type_val) { dispatch_color(color_val, [type_val]<Color C>() { if (type_val == static_cast<int>(Type::Car)) { process<C, Type::Car>(); } else if (type_val == static_cast<int>(Type::Bike)) { process<C, Type::Bike>(); } else { throw std::invalid_argument("无效的Type值"); } }); }
进阶优化:把第二个枚举也做成通用分发器
要是以后还要加更多模板参数,或者想复用Type的分发逻辑,可以给Type也写个分发器,代码会更整洁:
// 通用的Type分发器 template<typename Callback> void dispatch_type(int type_val, Callback&& callback) { if (type_val == static_cast<int>(Type::Car)) { callback.template operator()<Type::Car>(); } else if (type_val == static_cast<int>(Type::Bike)) { callback.template operator()<Type::Bike>(); } else { throw std::invalid_argument("无效的Type值"); } } // 更通用的双分发,检查次数依然是n+m void better_dispatch(int color_val, int type_val) { dispatch_color(color_val, [type_val]<Color C>() { dispatch_type(type_val, []<Type T>() { process<C, T>(); }); }); }
为啥这方法管用?
因为一旦进入dispatch_color的某个分支,C就变成了编译期确定的模板参数,后续调用process的时候完全没有运行时开销,完美符合低延迟系统的要求。而且新增枚举值的时候,只需要修改对应的分发器,不用动业务逻辑,维护起来也轻松多了。
内容来源于stack exchange
相关产品推荐
相关产品推荐

