C++17中将循环索引用作constexpr/模板参数的实现方案
语言与标准:C++17
需求描述
我创建了一个由constexpr构造的全局数组(例如int List[someconstant]),其元素可作为模板参数或传入其他constexpr函数、变量。现有如下for循环:
for(int i = 0; i < __MYCONST__ ; i++){ int j = List[i]; function<j>(/*循环外声明的部分数据*/); }
其中__MYCONST__是编译时常量(constexpr全局变量),我已成功将其作为模板参数在代码其他位置使用。我希望写成constexpr int j = List[i];或直接function<List[i]>();,但因i被视为变量而失败。i在循环内未被修改,且循环边界为编译时已知,因此我认为存在实现constexpr int j的方法。
问题
- 可替换当前for循环的何种构造能让我声明
constexpr的j? - 是否存在更简便的方式,调用模板参数依赖于
i(通过constexpr数组)的模板函数,无需声明constexpr的j并将其作为模板参数传递?
备注:无法将List[i]作为函数参数传递,必须用作模板参数。
已尝试实现
我已实现一个constexpr_loop函数:
template <int i, int N, typename FunctorType> struct basic_recursive{ basic_recursive(FunctorType fun){ fun(i); basic_recursive<i+1,N,FunctorType> a(fun); }; }; template <int N, typename FunctorType> struct basic_recursive<N,N,FunctorType>{ basic_recursive(FunctorType fun){}; }; template<int i, int N, typename FunctorType> void constexpr_for(FunctorType fun){ basic_recursive<i,N,FunctorType> a(fun); }
调用方式为constexpr_for([&](int i) {循环体}),该实现通过模板函数处理参数推导、模板结构体偏特化处理终止逻辑,二者配合完成递归循环。
我希望将其适配为支持带非类型模板参数的FunctorType,即循环索引作为模板参数,在构造函数中调用fun<i>()而非fun(i)。尝试使用C++20模板lambda但无法实现模板参数推导。
最小可复现示例(MWE)
我尝试实现了以下代码:
template <int i, int N, typename FunctorType> struct basic_recursive2{ basic_recursive2(FunctorType fun){ fun(); basic_recursive2<i+1,N,FunctorType> a(fun); }; }; template <int N, typename FunctorType> struct basic_recursive2<N,N,FunctorType>{ basic_recursive2(FunctorType fun){}; }; template<int i, int N, template<int> typename FunctorType> void constexpr_for2(FunctorType<i> fun){ basic_recursive2<i,N,FunctorType<i>> a(fun); }
主函数中调用:
constexpr_for2<1,3>([&]<int i>{printf("print in temp lambda %d \n",i);});
但编译报错:
file.cxx: error: no matching function for call to 'constexpr_for2' constexpr_for2<1,3>([&]<int i>{printf("print in temp lambda %d \n",i);}); ^~~~~~~~~~~~~~~~~~~ file.cxx: note: candidate template ignored: could not match 'FunctorType<1>' against '(lambda at file.cxx)' void constexpr_for2(FunctorType<i> fun){ ^ file.cxx: note: candidate template ignored: invalid explicitly-specified argument for template parameter 'FunctorType' void constexpr_for2(FunctorType<i> fun){ ^
解决方案
@user17732522提出以下解决方案:
[&]<int... j>(std::integer_sequence<int, j...>{ (function<List[j]>(/*循环外声明的部分数据*/), ...); }(std::make_integer_sequence<int, __MYCONST__>{});
该方案通过模板lambda结合std::make_integer_sequence生成编译时整数序列,利用参数包展开实现循环,将每个序列元素作为模板参数调用function<List[j]>。
我将其适配为支持多语句循环体的版本,通过嵌套模板lambda实现:
[&]<int... j>(std::integer_sequence<int, j...>{ ( [&]<int i>{ 依赖i的指令1; 依赖i的指令2;... }.template operator()<j>(),...); }(std::make_integer_sequence<int, __MYCONST__>{});
该实现带来显著性能提升,调用的模板化函数性能提升2.5至4倍。
内容的提问来源于stack exchange,提问作者Sardine

