2013-10-19 78 views
1

假设我的代码:C++模板递归停止条件

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创建单独的专业化?

回答

3

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

这是指actLoop如果num0同一实例,因此打破了无限的实例。

+0

哇,这真是我想不到的好方法。 – Ruslan

5

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

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

+0

的问题是源的紧凑性和避免重复的代码中的一个。 – Ruslan

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的情况。

+0

'typename std :: conditional <(num> 0),std :: true_type,std :: false_type> :: type()'或'std :: integral_constant 0)> {}'稍微短一些;) – dyp

+0

“A way不会阻止完整内联,最终递归调用是:“没有最终的递归调用,但优化器要抛出该分支'if(0> 0)'。你编译它,看看它是否内联? – dyp

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

虽然你的例子展示了代码中循环生成魔法的良好分离,但我的问题是关于避免显式的特化。不过我必须承认,你的代码比我的终端更具可读性,所以+1。 – Ruslan

+0

谢谢。是的,它远远满足你所要求的我不会发布它,除了我认为大多数似乎需要的地方(如果)()风格的诡计会更好这个。 – jthill