简体   繁体   English

C++模板递归停止条件

[英]C++ template recursion stop condition

Suppose I have the code:假设我有代码:

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);
    }
}

It should create nested loop with nest level of num .它应该创建嵌套级别为num嵌套循环。 When I try to compile it, I get compiler error about too deep recursion, ie seems compiler doesn't eliminate if(0>0) statement.当我尝试编译它时,我收到关于递归太深的编译器错误,即似乎编译器没有消除 if(0>0) 语句。

Is there a good way to make this happen, not having to create separate specialization for num=0 ?有没有一种好方法可以实现这一点,而不必为num=0创建单独的专业化?

The if( num > 0 ) is a runtime condition. if( num > 0 )是运行时条件。 The recursion is happening at compile time.递归发生在编译时。 So no, there is no way to avoid the specialization for num = 0 .所以不,没有办法避免num = 0

Why is it a problem though, to create the specialization for num = 0 ?为什么会出现问题,为num = 0创建特化?

There's a way, Andrei Alexandrescu has presented it in one of his talks at Going Native 2013 :有一种方法,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);
    }
}

This refers to the same instantiation of actLoop if num is 0 , therefore breaking the infinite instantiation.如果num0 ,这指的是相同的actLoop实例化,因此打破了无限实例化。

The clearest indication that something's wrong here is num .最清楚地表明这里有问题的是num There's a difference between nonce names you don't need to describe and confused names you can't.您不需要描述的 nonce 名称和您不能描述的混淆名称之间存在差异。 You're trying to make num mean two different things, the number of loop layers remaining and the array index you're using for bookkeeping.你试图让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);
}

A way that doesn't block full inlining with a final recursive call is:一种不会通过最终递归调用阻止完全内联的方法是:

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());
   }
}

where we call a different overload on the -1 case than on the other cases.我们在 -1 情况下调用与其他情况不同的重载。

Another approach would be to bounce the function call to a template class , where you specialize around the num = -1 case.另一种方法是将函数调用反弹到template class ,您可以在其中专门处理num = -1情况。

With modern C++ (since C++17) this problem can be solved much more straightforwardly: by using if constexpr statement.使用现代 C++(自 C++17)可以更直接地解决这个问题:通过使用if constexpr语句。 Here's the modified version of the code in the OP (with some dummy wrapper to make it compilable):这是 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);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM