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

如何实现遍历多个Span笛卡尔积的可变参数loop函数(C++20)

实现可变参数loop函数遍历Span笛卡尔积

背景定义

Span 指代连续内存(比如数组),其类定义如下:

template <typename T>
class Span {
public:
    Span(T first, size_t s) : first(first), last(first + s) {}
    Span(T first, T last) : first(first), last(last) {}
public:
    // 迭代底层连续内存
    T begin() const { return first; }
    T end() const { return last; }
    auto& operator[](size_t idx) const { return *(first + idx); }
    size_t size() const { return last - first; }
private:
    T first;
    T last;
};

(注:补充size()方法用于获取Span长度,原定义未显式给出但实现逻辑必需)

需求说明

需要实现一个可变参数loop函数,模拟嵌套循环遍历所有传入Span的笛卡尔积,对每个组合调用传入的Func函数处理。函数原型如下:

template <typename Func, typename ...Spans>
void loop(Func func, Spans &&...spans) {
    // 实现部分
}

示例用法

#include <iostream>

void func(int a, float b) {
    std::cout << a << ", " << b << std::endl;
}

void func(int a, float b, int c) {
    std::cout << a << ", " << b << ", " << c << std::endl;
}

int main() {
    int arr[] {1, 2, 3};
    float arr2[] {1.1, 2.2};
    int arr3[] {-1, -2, -3};
    
    loop(func, Span(arr, 3), Span(arr2, 2));
    /* 输出结果:
        1, 1.1
        1, 2.2
        2, 1.1
        2, 2.2
        3, 1.1
        3, 2.2
    */

    loop(func, Span(arr, 3), Span(arr2, 2), Span(arr3, 3));
    /* 输出结果:
        1, 1.1, -1
        1, 1.1, -2
        1, 1.1, -3
        1, 2.2, -1
        ...(剩余所有笛卡尔积组合)
    */
}

实现要求

  • 基于C++20及以下特性开发
  • 禁止使用标准库/第三方库(题目允许的vector、tuple、结构化绑定除外)
  • 禁止递归调用,优先使用循环实现

实现代码

#include <tuple>
#include <vector>

// Span类实现如背景定义所示

template <typename Func, typename ...Spans>
void loop(Func func, Spans &&...spans) {
    // 用tuple存储所有传入的Span
    auto span_tuple = std::forward_as_tuple(std::forward<Spans>(spans)...);
    constexpr size_t span_count = sizeof...(Spans);

    // 存储每个Span当前遍历的索引
    std::vector<size_t> indices(span_count, 0);
    // 存储每个Span的长度
    std::vector<size_t> sizes{spans.size()...};

    while (true) {
        // 展开索引序列,打包当前组合的参数并调用func
        [&]<size_t... Is>(std::index_sequence<Is...>) {
            func(std::get<Is>(span_tuple)[indices[Is]]...);
        }(std::make_index_sequence<span_count>{});

        // 索引进位逻辑:模拟嵌套循环的递增与进位
        size_t current = span_count - 1;
        while (current < span_count) {
            indices[current]++;
            if (indices[current] < sizes[current]) {
                break;
            }
            indices[current] = 0;
            if (current == 0) {
                // 所有Span遍历完成,退出循环
                return;
            }
            current--;
        }
    }
}

实现思路

  1. 数据存储:用tuple保存所有传入的Span,用两个vector分别记录每个Span的长度和当前遍历的索引。
  2. 索引进位:从最后一个Span的索引开始递增,当索引达到Span长度时重置为0并向前一个Span进位,直到第一个Span完成循环后终止遍历。
  3. 参数调用:利用C++11的索引序列(index_sequence)展开tuple中的Span和对应索引,将参数打包传递给func。

内容的提问来源于stack exchange,提问作者Ashcoll Ash

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.14 07:04:51