如何实现遍历多个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--; } } }
实现思路
- 数据存储:用
tuple保存所有传入的Span,用两个vector分别记录每个Span的长度和当前遍历的索引。 - 索引进位:从最后一个Span的索引开始递增,当索引达到Span长度时重置为0并向前一个Span进位,直到第一个Span完成循环后终止遍历。
- 参数调用:利用C++11的索引序列(
index_sequence)展开tuple中的Span和对应索引,将参数打包传递给func。
内容的提问来源于stack exchange,提问作者Ashcoll Ash
相关产品推荐
相关产品推荐

