3

假设我有代码:

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>0) 语句。

有没有一种好方法可以实现这一点,而不必为num=0.

4

5 回答 5

5

有一种方法,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);
    }
}

这指的是actLoopif numis的相同实例化0,因此打破了无限实例化。

于 2013-10-19T13:12:30.893 回答
5

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

但是,为什么创建专业化是一个问题num = 0

于 2013-10-19T12:52:48.447 回答
3

此处出现问题的最明显迹象是num. 您不需要描述的现时名称和您不能描述的混淆名称之间存在差异。你试图让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);
}
于 2013-10-19T16:56:37.233 回答
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 情况下称为与其他情况不同的重载。

另一种方法是将函数调用反弹到 a template class,您可以在其中专注于num = -1案例。

于 2013-10-19T13:44:03.657 回答
0

使用现代 C++(自 C++17 起)可以更直接地解决这个问题:通过 usingif 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);
}
于 2020-10-11T20:23:20.793 回答