You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 00:15:53