如何根据参数从结构体中指定数组取值?能否用SFINAE优化?
问题描述
定义了如下包含三个数组的结构体:
struct MyArrys { int arr1[3]; int arr2[3]; int arr3[3]; };
需要在如下函数中,根据step参数(取值仅为1、2、3)获取对应数组的指定索引值:
int doSomething(int x,int y, MyArrs arrs, const int step, const int idx) { int z = // 获取arr1、arr2或arr3对应索引的值 return x+y+z; }
已通过带switch分支的getArrayValue函数实现该功能:
int getArrayValue(const MyArrs& arrs, const int idx, const int step) { switch(step) { case 0: return arrs.arr1[idx]; case 1: return arrs.arr2[idx]; case 2: return arrs.arr3[idx]; } } int doSomething(int x,int y, MyArrs arrs, const int idx, const int step) { int z = getArrayValue(arrs,idx,step); return x+y+z; }
该实现可正常运行,现询问:
- 是否有更优方案?
- 能否使用SFINAE实现?
- 使用SFINAE是否有价值?
解决方案与分析
一、更优方案
1. 数组指针映射表
用静态数组存储三个成员数组的指针,直接通过step索引访问,替代switch分支:
int doSomething(int x, int y, MyArrs arrs, const int step, const int idx) { static constexpr int* MyArrs::* arr_ptrs[] = {&MyArrs::arr1, &MyArrs::arr2, &MyArrs::arr3}; // 因为step取值为1、2、3,所以要转成0、1、2的索引 int z = arrs.*arr_ptrs[step-1][idx]; return x + y + z; }
这种方式编译期就能确定映射关系,运行时直接索引,效率和switch相当,但代码更简洁,扩展性更好——后续新增数组成员只需在映射表中添加元素即可。
2. 重构结构体(若允许修改)
如果可以调整结构体定义,把三个数组合并为二维数组,直接通过step索引:
struct MyArrs { int arr[3][3]; // arr[0]对应原arr1,arr[1]对应原arr2,arr[2]对应原arr3 }; int doSomething(int x, int y, MyArrs arrs, const int step, const int idx) { int z = arrs.arr[step-1][idx]; return x + y + z; }
这是最直接的方案,代码最简洁,访问效率最高。
二、SFINAE的可行性与价值
1. 能否用SFINAE实现?
可以,但必须要求step是编译期常量(比如作为模板参数)。因为SFINAE是编译期机制,无法处理运行时传入的step变量。示例代码如下:
#include <type_traits> template<int Step> std::enable_if_t<Step == 1, int> getArrayValue(const MyArrs& arrs, int idx) { return arrs.arr1[idx]; } template<int Step> std::enable_if_t<Step == 2, int> getArrayValue(const MyArrs& arrs, int idx) { return arrs.arr2[idx]; } template<int Step> std::enable_if_t<Step == 3, int> getArrayValue(const MyArrs& arrs, int idx) { return arrs.arr3[idx]; } template<int Step> int doSomething(int x, int y, MyArrs arrs, const int idx) { int z = getArrayValue<Step>(arrs, idx); return x + y + z; }
2. 使用SFINAE是否有价值?
几乎没有价值,原因如下:
- 原需求中
step是运行时参数,SFINAE无法适配这种场景,只能用于step为编译期常量的情况,和原需求的使用场景不匹配。 - 即使
step是编译期常量,用C++17的if constexpr或者模板特化,写法比SFINAE更简洁直观:
template<int Step> int getArrayValue(const MyArrs& arrs, int idx) { if constexpr (Step == 1) { return arrs.arr1[idx]; } else if constexpr (Step == 2) { return arrs.arr2[idx]; } else { // Step ==3 return arrs.arr3[idx]; } }
- SFINAE写法繁琐,可读性差,反而增加代码维护成本,对于这种简单分支场景完全没必要。
内容的提问来源于stack exchange,提问作者Ali Ibrahim
相关产品推荐
相关产品推荐

