如何用有状态Lambda转换整数序列实现数字字面量运算符模板
原代码问题
原有实现中折叠表达式对weight的重复修改存在未定义行为隐患,虽依赖逗号运算符的左序求值暂时可用,但可读性差且会触发编译器警告,同时std::pow在constexpr上下文的可用性受编译器版本限制。
核心需求实现:有状态lambda映射整数序列
可以借助boost::mp11::mp_for_each遍历整数序列,配合可变捕获的有状态lambda完成权值的计算和映射,示例实现如下:
#include <string_view> #include <cstdint> #include <iostream> #include <boost/mp11/integer_sequence.hpp> #include <boost/mp11/algorithm.hpp> using namespace boost::mp11; template <char... Cs> [[nodiscard]] constexpr auto operator""_c(){ constexpr int len = sizeof...(Cs); // 生成0到len-1的整数序列 using seq = make_index_sequence<len>; int val = 0; int weight = 1; // 反向遍历整数序列,从最低位开始计算权值,避免除法和pow调用 mp_for_each<mp_reverse<seq>>([&](auto I){ val += (Cs - '0') * weight; weight *= 10; }); return val; } int main(){ std::cout << 0_c << std::endl; std::cout << 00_c << std::endl; std::cout << 01_c << std::endl; std::cout << 123_c << std::endl; }
如果需要把<0,1,2>这类序列直接转换为<100,10,1>的编译期整数序列,可以用编译期计算的方式实现:
template<size_t N, size_t... Is> constexpr auto make_weight_seq_impl(std::index_sequence<Is...>) { return index_sequence<((void)Is, size_t(std::pow(10, N - 1 - Is)))...>{}; } template<size_t N> constexpr auto make_weight_seq() { return make_weight_seq_impl<N>(std::make_index_sequence<N>{}); } // 调用示例,生成<100,10,1>序列 using weight_seq_3 = decltype(make_weight_seq<3>());
优化建议
- 完全避开
std::pow调用,所有整数权值都可以用编译期乘法计算,保证constexpr上下文的兼容性 - 反向遍历计算权值的方式不需要做除法,性能更高也不会出现整数截断问题
- 如果需要完全编译期求值,可以把权值计算全部放到模板元编程逻辑中,运行时直接返回常量值
内容的提问来源于stack exchange,提问作者NoSenseEtAl
相关产品推荐
相关产品推荐

