C++非类型模板与预处理对比:嵌套循环代码复用方案咨询
解决方案
方案1:非类型模板参数+成员指针
利用非类型模板参数指定目标数组成员、循环范围和索引偏移,让编译器自动生成不同版本的循环代码,既避免重复编码,又保证可读性:
#include <cstddef> struct myStruct { int iF[10][9]; int jF[9][10]; int someOtherArray[9][9]; }; // 核心循环模板:通过模板参数传递可变项 template<int (myStruct::*TargetArray)[10][9], int i_start, int i_end, int j_start, int j_end, int src_i_offset, int src_j_offset> void coreLoop(myStruct& b) { for(int i = i_start; i < i_end; ++i) { for(int j = j_start; j < j_end; ++j) { (b.*TargetArray)[i][j] = b.someOtherArray[i + src_i_offset][j + src_j_offset] * 2; } } } void function(myStruct &b){ // 生成第一个循环逻辑 coreLoop<&myStruct::iF, 1, 10, 0, 9, -1, 0>(b); // 生成第二个循环逻辑 coreLoop<&myStruct::jF, 0, 9, 1, 10, 0, -1>(b); }
方案2:Lambda封装可变逻辑
无需额外定义模板,在函数内部用Lambda封装循环中可变的部分(目标数组访问、索引转换),核心循环只负责遍历:
#include <utility> struct myStruct { int iF[10][9]; int jF[9][10]; int someOtherArray[9][9]; }; void function(myStruct &b){ // 封装核心循环逻辑 auto runLoop = [&](int iStart, int iEnd, int jStart, int jEnd, auto&& targetAccessor, auto&& srcIConv, auto&& srcJConv) { for(int i = iStart; i < iEnd; ++i) { for(int j = jStart; j < jEnd; ++j) { targetAccessor(b, i, j) = b.someOtherArray[srcIConv(i)][srcJConv(j)] * 2; } } }; // 调用第一个循环 runLoop(1, 10, 0, 9, [](myStruct& b, int i, int j) -> int& { return b.iF[i][j]; }, [](int i) { return i - 1; }, [](int j) { return j; }); // 调用第二个循环 runLoop(0, 9, 1, 10, [](myStruct& b, int i, int j) -> int& { return b.jF[i][j]; }, [](int i) { return i; }, [](int j) { return j - 1; }); }
方案3:维度处理器类模板(适配三维扩展)
针对你一维转三维的实际场景,用类模板特化来封装不同维度的逻辑,核心循环只写一次,扩展新维度仅需添加对应特化:
struct myStruct { int iF[10][9]; int jF[9][10]; int kF[9][9]; // 三维扩展新增数组 int someOtherArray[9][9]; }; // 维度标签 struct DimI {}; struct DimJ {}; struct DimK {}; // 维度处理器模板,默认空实现 template<typename Dim> struct DimensionProcessor; // I维度特化 template<> struct DimensionProcessor<DimI> { static constexpr int iStart = 1; static constexpr int iEnd = 10; static constexpr int jStart = 0; static constexpr int jEnd = 9; static int& getTarget(myStruct& b, int i, int j) { return b.iF[i][j]; } static int srcI(int i) { return i - 1; } static int srcJ(int j) { return j; } }; // J维度特化 template<> struct DimensionProcessor<DimJ> { static constexpr int iStart = 0; static constexpr int iEnd = 9; static constexpr int jStart = 1; static constexpr int jEnd = 10; static int& getTarget(myStruct& b, int i, int j) { return b.jF[i][j]; } static int srcI(int i) { return i; } static int srcJ(int j) { return j - 1; } }; // K维度特化(三维扩展时新增) template<> struct DimensionProcessor<DimK> { static constexpr int iStart = 0; static constexpr int iEnd = 9; static constexpr int jStart = 0; static constexpr int jEnd = 9; static int& getTarget(myStruct& b, int i, int j) { return b.kF[i][j]; } static int srcI(int i) { return i; } static int srcJ(int j) { return j; } }; // 核心循环函数 template<typename Dim> void processDimension(myStruct& b) { using Proc = DimensionProcessor<Dim>; for(int i = Proc::iStart; i < Proc::iEnd; ++i) { for(int j = Proc::jStart; j < Proc::jEnd; ++j) { Proc::getTarget(b, i, j) = b.someOtherArray[Proc::srcI(i)][Proc::srcJ(j)] * 2; } } } void function(myStruct &b){ processDimension<DimI>(b); processDimension<DimJ>(b); processDimension<DimK>(b); // 三维扩展时仅需添加此行 }
方案选型建议
- 若场景固定、参数明确,选方案1,编译期生成代码,性能最优。
- 若需临时调整逻辑、灵活度优先,选方案2,无需额外模板定义。
- 若需扩展至多维度(如你的三维物理场景),选方案3,结构清晰,扩展成本极低。
内容的提问来源于stack exchange,提问作者Kschau
相关产品推荐
相关产品推荐

