C++17及更早版本中带默认值的数组模板参数替代方案
兼容C++17及更早版本的3D空间隐式索引访问器模板实现方案
针对你需要的编译期3D空间索引转换访问器,在无法使用C20 std::array 默认模板参数的情况下,以下两种方案可以实现和C20方案同等的可读性,同时兼容C++17及更早版本:
方案一:标签式参数分组模板
将原点、填充、分块这三类语义相关的参数,分别用独立的模板标签结构体打包,主模板接收这些标签作为参数并提供默认值,既保留参数语义,又避免零散的模板参数列表。
实现代码
// 标签结构体:用于分组语义相关的编译期参数 template<int X = 0, int Y = 0, int Z = 0> struct Origin {}; template<int Left = 0, int Right = 0, int Top = 0, int Bottom = 0, int Front = 0, int Back = 0> struct Padding {}; template<int SizeX = 1, int SizeY = 1, int SizeZ = 1> struct Block {}; // 主访问器模板 template<typename OriginTag = Origin<>, typename PaddingTag = Padding<>, typename BlockTag = Block<>> struct Implicit3DIndexAccessor { // 辅助模板:提取标签中的参数值 template<typename T> struct GetOrigin; template<int X, int Y, int Z> struct GetOrigin<Origin<X,Y,Z>> { static constexpr int x = X; static constexpr int y = Y; static constexpr int z = Z; }; template<typename T> struct GetPadding; template<int L, int R, int T, int B, int F, int Ba> struct GetPadding<Padding<L,R,T,B,F,Ba>> { static constexpr int left = L; static constexpr int right = R; static constexpr int top = T; static constexpr int bottom = B; static constexpr int front = F; static constexpr int back = Ba; }; template<typename T> struct GetBlock; template<int X, int Y, int Z> struct GetBlock<Block<X,Y,Z>> { static constexpr int size_x = X; static constexpr int size_y = Y; static constexpr int size_z = Z; }; // 导出编译期常量参数 static constexpr int origin_x = GetOrigin<OriginTag>::x; static constexpr int origin_y = GetOrigin<OriginTag>::y; static constexpr int origin_z = GetOrigin<OriginTag>::z; static constexpr int pad_left = GetPadding<PaddingTag>::left; static constexpr int pad_right = GetPadding<PaddingTag>::right; static constexpr int pad_top = GetPadding<PaddingTag>::top; static constexpr int pad_bottom = GetPadding<PaddingTag>::bottom; static constexpr int pad_front = GetPadding<PaddingTag>::front; static constexpr int pad_back = GetPadding<PaddingTag>::back; static constexpr int block_size_x = GetBlock<BlockTag>::size_x; static constexpr int block_size_y = GetBlock<BlockTag>::size_y; static constexpr int block_size_z = GetBlock<BlockTag>::size_z; // 核心索引转换逻辑示例 template<int X, int Y, int Z> static constexpr size_t convert_index() { int adjusted_x = X - origin_x + pad_left; int adjusted_y = Y - origin_y + pad_top; int adjusted_z = Z - origin_z + pad_front; // 此处根据实际分块逻辑调整计算方式 return (adjusted_z / block_size_z) * ((pad_left + pad_right) / block_size_x) * ((pad_top + pad_bottom) / block_size_y) + (adjusted_y / block_size_y) * ((pad_left + pad_right) / block_size_x) + (adjusted_x / block_size_x); } };
使用示例
// 使用全部默认参数(原点(0,0,0)、无填充、块大小1x1x1) using DefaultAccessor = Implicit3DIndexAccessor<>; // 自定义原点、填充和分块参数 using CustomAccessor = Implicit3DIndexAccessor<Origin<5,5,5>, Padding<2,2,2,2,2,2>, Block<4,4,4>>; // 编译期计算转换后的索引 constexpr auto idx = CustomAccessor::convert_index<10,10,10>();
方案二:编译期参数结构体+模板特化
定义一个包含所有参数的编译期结构体,主模板接收该结构体类型作为参数并提供默认实现,参数集中管理,代码更简洁。
实现代码
// 默认参数结构体:所有参数的默认值 struct Default3DParams { static constexpr int origin_x = 0; static constexpr int origin_y = 0; static constexpr int origin_z = 0; static constexpr int pad_left = 0; static constexpr int pad_right = 0; static constexpr int pad_top = 0; static constexpr int pad_bottom = 0; static constexpr int pad_front = 0; static constexpr int pad_back = 0; static constexpr int block_size_x = 1; static constexpr int block_size_y = 1; static constexpr int block_size_z = 1; }; // 辅助宏(可选):简化自定义参数结构体的定义 #define DEFINE_3D_PARAMS(Name, Ox, Oy, Oz, Pl, Pr, Pt, Pb, Pf, Pb_, Bsx, Bsy, Bsz) \ struct Name { \ static constexpr int origin_x = Ox; \ static constexpr int origin_y = Oy; \ static constexpr int origin_z = Oz; \ static constexpr int pad_left = Pl; \ static constexpr int pad_right = Pr; \ static constexpr int pad_top = Pt; \ static constexpr int pad_bottom = Pb; \ static constexpr int pad_front = Pf; \ static constexpr int pad_back = Pb_; \ static constexpr int block_size_x = Bsx; \ static constexpr int block_size_y = Bsy; \ static constexpr int block_size_z = Bsz; \ } // 自定义参数示例 DEFINE_3D_PARAMS(Custom3DParams, 5,5,5, 2,2,2,2,2,2, 4,4,4); // 主访问器模板 template<typename Params = Default3DParams> struct Implicit3DIndexAccessor { // 直接复用参数结构体中的编译期常量 static constexpr int origin_x = Params::origin_x; static constexpr int origin_y = Params::origin_y; static constexpr int origin_z = Params::origin_z; static constexpr int pad_left = Params::pad_left; static constexpr int pad_right = Params::pad_right; static constexpr int pad_top = Params::pad_top; static constexpr int pad_bottom = Params::pad_bottom; static constexpr int pad_front = Params::pad_front; static constexpr int pad_back = Params::pad_back; static constexpr int block_size_x = Params::block_size_x; static constexpr int block_size_y = Params::block_size_y; static constexpr int block_size_z = Params::block_size_z; // 核心索引转换逻辑示例 template<int X, int Y, int Z> static constexpr size_t convert_index() { int adjusted_x = X - origin_x + pad_left; int adjusted_y = Y - origin_y + pad_top; int adjusted_z = Z - origin_z + pad_front; // 此处根据实际分块逻辑调整计算方式 return (adjusted_z / block_size_z) * ((pad_left + pad_right) / block_size_x) * ((pad_top + pad_bottom) / block_size_y) + (adjusted_y / block_size_y) * ((pad_left + pad_right) / block_size_x) + (adjusted_x / block_size_x); } };
使用示例
// 使用默认参数 using DefaultAccessor = Implicit3DIndexAccessor<>; // 使用自定义参数结构体 using CustomAccessor = Implicit3DIndexAccessor<Custom3DParams>; // 编译期计算转换后的索引 constexpr auto idx = CustomAccessor::convert_index<10,10,10>();
方案对比
- 标签分组方案:参数语义划分清晰,修改单个分组参数时无需重新定义整个参数集合,适合参数组合频繁变化的场景。
- 结构体参数方案:参数集中管理,代码更简洁,适合参数组合相对固定的场景。
内容的提问来源于stack exchange,提问作者比尔盖子
相关产品推荐
相关产品推荐

