C ++模板递归停止条件

时间:2013-10-19 12:50:44

标签: c++ templates recursion

假设我有代码:

template<size_t num> void actLoop(float* result, const float* rvector,
                                          size_t* xs, size_t indexIn=0)
{
    for(xs[num]=0; xs[num]<N; ++xs[num])
    {
        size_t index = indexIn+xs[num]*strides[num];
        if(num>0)
            actLoop<num-1>(result,rvector,xs,index);
        else
            result[index] = work(rvector,index,xs);
    }
}

它应该创建嵌套级别为num的嵌套循环。当我尝试编译它时,我得到有关递归过深的编译器错误,即编译器似乎没有消除if(0&gt; 0)语句。

是否有一种很好的方法可以实现这一目标,而不必为num=0创建单独的专业化?

5 个答案:

答案 0 :(得分:5)

if( num > 0 )是运行时条件。递归发生在编译时。所以不,没有办法避免num = 0的专业化。

为什么创建num = 0的专业化?

是一个问题

答案 1 :(得分:3)

有一种方法,Andrei Alexandrescu在Going Native 2013的一次演讲中提出了这个问题:

template<size_t num> void actLoop(float* result, const float* rvector,
                                          size_t* xs, size_t indexIn=0)
{
    for(xs[num]=0; xs[num]<N; ++xs[num])
    {
        size_t index = indexIn+xs[num]*strides[num];
        if(num>0)
            actLoop<(num > 0 ? num-1 : num)>(result,rvector,xs,index);
        else
            result[index] = work(rvector,index,xs);
    }
}

如果actLoopnum,则指的是0的同一实例化,从而打破了无限的实例化。

答案 2 :(得分:2)

这里出现问题的最明显迹象是num。你不需要描述的nonce名称和你不能混淆的名字之间存在差异。你试图使num意味着两个不同的东西,剩下的循环层数和你用于簿记的数组索引。

template<size_t nloops>
void actLoop(float* result, const float* rvector, size_t* xs, size_t index=0)
{       // loop layers (nloops>=1): loop
        auto xs_index=nloops-1;
        for ( int i=0 ; i < N ; ++i ) {
                xs[xs_index] = i;
                actLoop<nloops-1>(result, rvector, xs, index + i*strides[xs_index]);
        }
}

template<>
void actLoop<0>(float* result, const float* rvector, size_t* xs, size_t index)
{       // no loops left: work
                result[index] = work(rvector,index,xs);
}

答案 3 :(得分:0)

不阻止使用最终递归调用进行完全内联的方法是:

template<size_t num> void actLoop(float* result, const float* rvector,
                                      size_t* xs, size_t indexIn=0, std::false_type )
{
  result[index] = work(rvector,index,xs);
}

template<size_t num> void actLoop(
    float* result,
    const float* rvector,
    size_t* xs,
    size_t indexIn=0,
    std::true_type unused=std::true_type()
  )
{
  for(xs[num]=0; xs[num]<N; ++xs[num])
  {
    size_t index = indexIn+xs[num]*strides[num];
    actLoop<num-1>(result, rvector, xs, index, typename std::conditional<(num>0), std::true_type, std::false_type>::type());
   }
}

我们在-1情况下调用不同的重载而不是其他情况。

另一种方法是将函数调用退回到template class,您可以专注于num = -1案例。

答案 4 :(得分:0)

使用现代C ++(自C ++ 17起)可以更直接地解决此问题:使用if constexpr语句。这是OP中代码的修改版本(带有一些虚拟包装程序以使其可编译):

#include <stddef.h>

float work(const float*,size_t,const size_t*) { return 0; }
const unsigned N=5;
size_t strides[10];

template<size_t num> void actLoop(float* result, const float* rvector,
                                          size_t* xs, size_t indexIn=0)
{
    for(xs[num]=0; xs[num]<N; ++xs[num])
    {
        size_t index = indexIn+xs[num]*strides[num];
        if constexpr(num>0)
            actLoop<num-1>(result,rvector,xs,index);
        else
            result[index] = work(rvector,index,xs);
    }
}

int main()
{
    float res[10], rvec[10]={};
    size_t xs[10], str[10]={};
    actLoop<5>(res,rvec,xs);
}