C++元编程:如何将vector正元素筛选存入unordered_map转编译期实现
核心误区说明
你当前报错的本质原因是:constexpr 要求表达式的所有操作数必须是编译期可确定的常量,而你用的运行时 std::vector 哪怕加了 const 修饰,它的元素存储在堆上、值只有运行时才能拿到,自然不满足 constexpr 的要求。
问题1:是否可以调整实现方式,让constexpr可以被正确声明?
可以,前提是你要处理的整个数组的元素值必须是编译期固定的,不能用运行时动态分配的std::vector,可以改用C11及以上支持的std::array,C20开始std::array可以声明为constexpr常量。
如果需要完全在编译期完成筛选计算,注意C++20之前的constexpr函数不支持动态内存分配,因此不能直接在编译期构造std::unordered_map,可以先把筛选结果存在固定大小的std::array里,运行时再转成unordered_map:
#include <array> #include <unordered_map> // 编译期固定的输入数组 constexpr std::array<long, 5> g_compile_arr = {-2, 3, -5, 7, 1}; constexpr auto compile_filter() { // 3为正数的个数,编译期可直接计算得到 std::array<std::pair<long, long>, 3> res{}; size_t idx = 0; for (size_t i = 0; i < g_compile_arr.size(); ++i) { if constexpr (g_compile_arr[i] > 0) { res[idx++] = {static_cast<long>(i), g_compile_arr[i]}; } } return res; } // 编译期就完成筛选计算 constexpr auto filtered_res = compile_filter(); // 运行时直接转成unordered_map即可,不需要再做判断和遍历 auto dic = std::unordered_map<long, long>(filtered_res.begin(), filtered_res.end());
问题2:有没有其他可行的实现方案可以实现需求?
分两种场景对应不同方案:
- 场景1:输入数组元素是编译期固定的
可以用C++17的折叠表达式实现编译期展开,完全消除运行时循环和分支:#include <utility> #include <unordered_map> // 实现:编译期展开参数包,自动过滤正数 template <long... Values, std::size_t... Indices> auto build_positive_map_impl(std::index_sequence<Indices...>) { std::unordered_map<long, long> dic; // 折叠表达式编译期展开,只有正数会被插入 ((Values > 0 ? dic.emplace(Indices, Values) : void()), ...); return dic; } template <long... Values> auto build_positive_map() { return build_positive_map_impl<Values...>(std::make_index_sequence<sizeof...(Values)>{}); } // 使用:直接把数组元素作为模板参数传入,编译期完成过滤 auto dic = build_positive_map<-2, 3, -5, 7, 1>(); - 场景2:输入数组是运行时动态生成的
这种场景下没有任何办法把元素的正负判断移到编译期,因为元素值只有运行时才能获取,你原本的运行时if写法就是最优实现。现代编译器的分支预测、指令重排优化会把运行时分支的开销降到极低,完全没有必要强行套用模板元编程消除分支,反而会带来不必要的复杂度。
内容的提问来源于stack exchange,提问作者albiremo
相关产品推荐
相关产品推荐

