2011-03-11 89 views
22

我發現一個代碼在這裏Printing 1 to 1000 without loop or conditionals編譯時間遞歸如何工作?

有人可以解釋時候怎麼編譯遞歸的作品,無法找到它在谷歌

// compile time recursion 
template<int N> void f1() 
{ 
    f1<N-1>(); 
    cout << N << '\n'; 
} 

template<> void f1<1>() 
{ 
    cout << 1 << '\n'; 
} 


int main() 
{ 
    f1<1000>(); 
} 

謝謝!

+0

其實有一個技巧,專業化是一個有條件的,雖然沒有'如果'關鍵字... – 2011-03-11 18:20:12

+0

有一個經驗法則比運行時遞歸更快嗎? – 2012-01-29 20:26:09

+0

使用此代替常規遞歸有什麼好處? – zzzzz 2013-06-10 08:03:23

回答

11

它重複實例化具有遞減值的f1<N>模板Nf1<N>()調用f1<N-1>等等)。 N==1的顯式特殊化結束遞歸:只要N變爲1,編譯器將選擇專用函數而不是模板化函數。

f1<1000>()導致編譯器實例化f1<N> 999次(不計在最後調用f1<1>)。這就是編譯代碼大量使用模板元編程技術需要一段時間的原因。

整件事情很大程度上依賴於編譯器的優化技巧 - 理想情況下,它應該完全刪除遞歸(僅用作hack來模擬for循環)。

2

它在概念上與運行時遞歸幾乎相同。 f1<1000>調用f1<999>,然後打印出1000. f1<999>調用f1<998>然後打印出999等。一旦達到1,模板專門化作爲中止遞歸的基本情況。

+0

爲什麼從0到1000打印而不是從1000打印到0? – VextoR 2011-03-11 14:43:20

+1

@VextoR:這是因爲它首先調用f1 ,然後打印。 – sharptooth 2011-03-11 14:46:51

+1

@VextoR因爲它在輸出'N'到控制檯後實例化下一個模板。 – Maxpm 2011-03-11 14:47:31

1

足夠簡單,每個模板實例都會使用更改後的參數創建一個新函數。就像你定義了:f1_1000(),f1_999()等等。

每個函數調用名稱少於1的函數。由於存在不同的模板,而不是遞歸,所以要定義f1_1(),我們也有一個停止事件。

1

這不能保證是純粹的編譯時遞歸。編譯器必須爲所有參數值從2到1000實例化函數f1(),它們將互相調用。

然後,編譯器可能會看到這些調用可以變成一系列cout << ...語句。也許它消除了調用,也許不是 - 這取決於編譯器。從C++的角度來說,這是一個函數調用鏈,編譯器可以做任何事情,只要它不會改變行爲。

1

您的因子計算解釋爲here

btw注意到你的函數不適用於負數。