2013-10-19 115 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