C++17:元组查找表未命中时如何触发编译期错误
编译期检查查找表匹配的实现方案
需求背景
现有一个基于元组的查找表,包含多种Entry类型,每个Entry通过fits函数判断是否匹配目标值,匹配则返回对应的setting值。需要实现当待搜索值不在查找表中时触发编译期错误的功能,但现有查找函数无法满足该需求。
现有查找函数
template <typename TLookup, typename TValueToSearch, typename TSetting> constexpr auto lookupNeededSetting(const TValueToSearch &valueToSearch) { TSetting setting = {}; typename TLookup::Type const table = typename TLookup::Type{}; std::apply( [&valueToSearch, &setting](const auto &...entry) { // 遍历表中每个条目 return (((entry.fits(valueToSearch)) ? setting = entry.value() : false) || ...); }, table); return setting; }
查找表定义
template <typename... TEntries> struct LookupTable { using Type = std::tuple<TEntries...>; ///< 表示条目元组的类型 };
尝试的实现及错误
尝试通过模板类结合constexpr逻辑实现编译期检查,但编译器报错tempSetting不是常量:
template <typename TLookup, typename TValueToSearch, typename TSetting, auto VValueToSearch> constexpr bool lookupNeededSetting2(TSetting& setting) { bool found = false; typename TLookup::Type const table = typename TLookup::Type{}; std::apply( [&found, &setting](const auto&... entry) { // 遍历表中每个条目 ((entry.fits(VValueToSearch) ? (setting = entry.value(), found = true) : false) || ...); }, table); return found; } template <typename TLookup, typename TValueToSearch, typename TSetting, auto VValueToSearch> class Lookup { public: static constexpr TSetting setting = [] { TSetting tempSetting{}; constexpr bool found = lookupNeededSetting2<TLookup, TValueToSearch, TSetting, VValueToSearch>(tempSetting); static_assert(found, "The given VValueToSearch was not found in the lookup table."); return tempSetting; }(); static constexpr auto value = setting; };
错误原因
lambda中的tempSetting是自动变量,并非常量表达式。lookupNeededSetting2调用时修改了该变量,导致整个函数调用无法成为常量表达式,因此无法用constexpr bool接收返回值,也无法满足static_assert对常量表达式的要求。
解决方案
将检查和查找逻辑拆分为两个独立的constexpr函数,确保所有操作在编译期完成,避免依赖运行时变量修改:
1. 编译期检查匹配条目是否存在
template <typename TLookup, auto VValueToSearch> constexpr bool hasMatchingEntry() { constexpr auto table = typename TLookup::Type{}; return std::apply([](const auto&... entry) { // 折叠表达式判断是否存在匹配条目 return (entry.fits(VValueToSearch) || ...); }, table); }
2. 编译期获取匹配的Setting值
template <typename TLookup, typename TSetting, auto VValueToSearch> constexpr TSetting getMatchingSetting() { constexpr auto table = typename TLookup::Type{}; TSetting setting{}; // 已通过hasMatchingEntry确认存在匹配,因此必然能找到对应条目 std::apply([&](const auto&... entry) { ((entry.fits(VValueToSearch) ? (setting = entry.value()) : void()), ...); }, table); return setting; }
3. 最终Lookup模板类
template <typename TLookup, typename TSetting, auto VValueToSearch> class Lookup { public: // 编译期断言:必须存在匹配条目 static_assert(hasMatchingEntry<TLookup, VValueToSearch>(), "The given value was not found in the lookup table."); // 编译期初始化匹配的Setting值 static constexpr TSetting value = getMatchingSetting<TLookup, TSetting, VValueToSearch>(); };
关键注意事项
- Entry类的
fits和value函数必须是constexpr,才能在编译期被调用。 - 拆分逻辑后,
hasMatchingEntry返回纯常量表达式,可直接用于static_assert触发编译期错误;getMatchingSetting在确认存在匹配的前提下,通过编译期遍历赋值,确保返回值是常量表达式。
内容的提问来源于stack exchange,提问作者tunguskar
相关产品推荐
相关产品推荐

