C++20遍历constexpr数组所有组合实例化模板函数的正确方法
遍历constexpr数组所有组合以实例化模板函数
问题背景
我们有如下C++20模板类:
template<int someSegmentSize> class SomeAbstractFileService { public: void init() { std::cout << "Initializing FileService with segment size: " << someSegmentSize << std::endl; } template<int HashNum> void create_shemas() { std::cout << "Creating with hash function number: " << HashNum << " and segment size: " << someSegmentSize << std::endl; } // 其他非模板函数... };
以及两个constexpr数组:
constexpr size_t N = 7; constexpr size_t N2 = 7; // 大小可以不同 constexpr std::array<int, N> segment_size_arr{-1, -2, -3, -4, -5, -6, -7}; constexpr std::array<int, N2> hash_num_arr{1, 2, 3, 4, 5, 6, 7};
需要遍历两个数组的所有数对,实例化并调用以下模板函数:
template<int SegmentSize, int Hash> void performStuff() { std::cout << SegmentSize << '\t' << Hash << '\n'; SomeAbstractFileService<SegmentSize> ss; ss.init(); ss.template create_shemas<Hash>(); // 必须使用新对象,避免 schema 更新冲突 }
原尝试代码出现编译错误:
Invalid operands to binary expression ('(lambda at /.../main.cpp:39:11)' and 'unsigned long') main2.cpp:35:5: note: in instantiation of function template specialization 'iterate_pairs()::(anonymous class)::operator()<0UL, 1UL, 2UL, 3UL, 4UL, 5UL, 6UL>' requested here main2.cpp:58:5: note: in instantiation of function template specialization 'iterate_pairs<int, 7UL, 7UL, std::array<int, 7UL>{{-1, -2, -3, -4, -5, -6, -7}}, std::array<int, 7UL>{{1, 2, 3, 4, 5, 6, 7}}>' requested here
错误原因
原代码的嵌套lambda存在冗余参数传递错误:内部lambda不必要地接收了外层的std::index_sequence<Is...>参数,且模板参数传递时类型不匹配,导致编译器无法正确解析lambda的调用方式。
正确实现(二元组遍历)
修正后的代码简化了lambda嵌套逻辑,直接通过两层折叠表达式实现笛卡尔积遍历:
#include <iostream> #include <array> #include <utility> template<int someSegmentSize> class SomeAbstractFileService { public: void init() { std::cout << "Initializing FileService with segment size: " << someSegmentSize << std::endl; } template<int HashNum> void create_shemas() { std::cout << "Creating with hash function number: " << HashNum << " and segment size: " << someSegmentSize << std::endl; } }; template<int SegmentSize, int Hash> void performStuff() { std::cout << SegmentSize << '\t' << Hash << '\n'; SomeAbstractFileService<SegmentSize> ss; ss.init(); ss.template create_shemas<Hash>(); } template<typename T, size_t N, size_t N2, const std::array<T, N>& SegArr, const std::array<T, N2>& HashArr> constexpr void iterate_pairs() { // 展开第一个数组的所有索引 []<std::size_t... SegIs>(std::index_sequence<SegIs...>) { // 对每个SegIs,展开第二个数组的所有索引 ([]<std::size_t... HashIs>(std::index_sequence<HashIs...>) { (performStuff<SegArr[SegIs], HashArr[HashIs]>(), ...); }(std::make_index_sequence<N2>{}), ...); }(std::make_index_sequence<N>{}); } int main(){ constexpr size_t N = 7; constexpr size_t N2 = 7; constexpr std::array<int, N> segment_size_arr{-1, -2, -3, -4, -5, -6, -7}; constexpr std::array<int, N2> hash_num_arr{1, 2, 3, 4, 5, 6, 7}; iterate_pairs<int, N, N2, segment_size_arr, hash_num_arr>(); }
关键改动:
- 外层lambda接收第一个数组的索引序列,对每个索引
SegIs,内层lambda接收第二个数组的索引序列HashIs - 通过两层折叠表达式
(..., (...))实现笛卡尔积的完全展开 - 移除了冗余的参数传递,确保模板参数和索引序列的类型匹配
推广到任意数量的constexpr数组
要支持三元组、四元组等任意数量数组的笛卡尔积遍历,可以通过递归模板和参数包实现通用解决方案:
通用笛卡尔积遍历模板
#include <iostream> #include <array> #include <utility> // 终止条件:无剩余数组时执行目标函数 template<typename Func, typename... Args> constexpr void apply_tuple(Func&& func, Args... args) { std::forward<Func>(func)(args...); } // 递归展开:遍历当前数组的所有元素,递归处理剩余数组 template<typename Func, typename T, size_t N, typename... RestArrays> constexpr void iterate_cartesian(Func&& func, const std::array<T, N>& arr, RestArrays&&... rest) { []<std::size_t... Is>(std::index_sequence<Is...>, auto&& func, const auto& arr, auto&&... rest) { (apply_tuple(std::forward<decltype(func)>(func), arr[Is], rest...), ...); }(std::make_index_sequence<N>{}, std::forward<Func>(func), arr, std::forward<RestArrays>(rest)...); } // 适配原performStuff的包装器 struct PerformStuffWrapper { template<int... Args> void operator()(std::integral_constant<int, Args>...) const { performStuff<Args...>(); } }; // 原有的SomeAbstractFileService和performStuff保持不变 template<int someSegmentSize> class SomeAbstractFileService { public: void init() { std::cout << "Initializing FileService with segment size: " << someSegmentSize << std::endl; } template<int HashNum> void create_shemas() { std::cout << "Creating with hash function number: " << HashNum << " and segment size: " << someSegmentSize << std::endl; } }; template<int SegmentSize, int Hash> void performStuff() { std::cout << SegmentSize << '\t' << Hash << '\n'; SomeAbstractFileService<SegmentSize> ss; ss.init(); ss.template create_shemas<Hash>(); } int main(){ constexpr std::array<int, 2> arr1{-1, -2}; constexpr std::array<int, 2> arr2{1, 2}; constexpr std::array<int, 2> arr3{10, 20}; // 新增第三个数组 // 遍历三元组组合 iterate_cartesian(PerformStuffWrapper{}, std::array{std::integral_constant<int, arr1[0]>{}, std::integral_constant<int, arr1[1]>{} }, std::array{std::integral_constant<int, arr2[0]>{}, std::integral_constant<int, arr2[1]>{} }, std::array{std::integral_constant<int, arr3[0]>{}, std::integral_constant<int, arr3[1]>{} }); }
说明:
- 用
std::integral_constant将数组的constexpr值转换为模板参数可用的编译期常量 iterate_cartesian通过递归展开每个数组的元素,最终调用目标函数的包装器- 该方案支持任意数量的constexpr数组,只需扩展传入的数组参数即可
内容的提问来源于stack exchange,提问作者AlecadM
相关产品推荐
相关产品推荐

