系統程式設計師的 C++

模板與泛型程式設計(templates and generic programming)

假設你寫了個求兩個 int 最大值的函式,現在你想要同樣的邏輯用在 double、字串、你自己的型別上。在 C 裡你可能會複製貼上,或改用 void* 而失去型別安全。C++ 模板讓你把程式碼寫一次,把型別留成待填的空白,編譯器則為你實際用到的每個型別蓋印出具體、經過完整型別檢查的版本。這就是泛型程式設計:以「某個型別 T」而非固定型別來書寫的演算法與資料結構。

模板是個食譜,本身不是程式碼。當你寫 template<class T> T max(T a, T b) {...} 時什麼都還沒被編譯;只有當你呼叫 max(3, 4) 時,編譯器才進行實例化(instantiation)——它代入 T = int 並產生一個真正的 max<int> 函式,然後對它做型別檢查。用 double 呼叫它,它就產生一個分開的 max<double>。你也可以寫特殊化(specialization):為某個特定型別手工調校的模板版本,編譯器會偏好它勝過通用食譜。模板作用於型別與編譯期常數值,而 std::vector、std::unique_ptr 與幾乎整個標準函式庫都是這樣寫成的——一個本體變成許多具體型別。

為何重要:模板給你泛型程式碼的抽象,搭配手寫、特定型別程式碼的速度,因為代入完全發生在編譯期——這是零開銷原則的支柱之一,有時稱為單型化(monomorphization)。誠實的代價:每個相異的實例化都產生分開的機器碼,可能讓二進位檔膨脹;模板編譯緩慢;而模板的錯誤訊息以冗長著稱,因為失敗常浮現在所產生程式碼的深處,而非你的呼叫處。Concepts(C++20)的加入很大程度上就是為了讓那些錯誤說出你究竟做錯了什麼。

template<class T> T max(T a, T b) { return a < b ? b : a; } // max(3,4) 實例化 max<int>;max(1.5,2.5) 實例化 max<double>

一個模板本體;編譯器為你實際呼叫的每個型別產生一個分開、經完整型別檢查的函式。

模板在被實例化前不是真正的程式碼,所以你從未用到的模板分支中的錯誤,可能直到有人用某型別觸發那個分支時才被發現。這也是為什麼大量模板程式碼可能悄悄拉長編譯時間並膨脹二進位檔大小。

又称
genericsparametric code泛型模板