為什麼需要模板?
C 語言巨集 #define min(a,b) ... 雖通用但無型別檢查、副作用風險。C++ inline 函數解決安全性,卻失去通用性 (需重載每種型別)。模板 = 型別安全 + 通用演算法。
模板函數基本語法
template
const T& min(const T& a, const T& b) {
return a > b ? b : a;
}
// 使用
min(10, 4); // T 推導為 int
min(5.3, 18.6); // T 推導為 double
模板類別語法
template
class Stack {
T* data; int top, capacity;
public:
Stack(int cap=100) : capacity(cap), top(-1) { data = new T[cap]; }
~Stack() { delete[] data; }
void push(const T& v) { data[++top] = v; }
T pop() { return data[top--]; }
};
// 具現化
Stack si;
Stack sd;
Stack ss;
模板參數類型
| 種類 | 語法 | 範例 | 限制 |
| 型別參數 | typename T / class T | template | 任何型別 |
| 非型別參數 | int N, bool Flag | template | 必須為編譯期常數 |
| 模板模板參數 | template class Container | template class C> | 接受模板本身 |
非型別參數範例
template
class FixedArray {
T data[MAX_SIZE];
public:
T& operator[](int i) { return data[i]; }
};
FixedArray arr; // 合法
// FixedArray arr2; // 錯誤:n 非常數
const int N = 50;
FixedArray arr3; // 合法
預設參數
template >
class Vector { ... };
規則同函數預設參數:有預設者之後皆須有預設。
模板定義與宣告分離問題
模板方法定義必須在標頭檔可見 (編譯器需實例化)。慣例:
// Stack.h
template class Stack {
void push(const T&); // 宣告
};
#include "Stack.inl" // 內含定義
// Stack.inl
template
void Stack::push(const T& v) { ... }
非 inline 虛擬函數亦可定義於 .inl,不隱含 inline。
普通類別中的成員模板
class Printer {
public:
template
void print(const T& v) { cout << v << endl; }
void print(int v) { cout << "int: " << v << endl; } // 非模板重載
};
進階技巧
1. 語法檢查延遲
模板本體語法錯誤在實例化時才報告。可在模板中假設參數型別具備特定操作 (如 operator>, .print()),實例化時才驗證。
2. 繼承與多態陷阱
template class Base { ... };
Base bi;
Base bd;
// bi 與 bd 無共同基類!無法 Base* pb = &bi; 實現多態
// 正確做法:抽象基類
class AbstractBase { virtual void foo() = 0; };
template
class Derived : public AbstractBase { ... };
// AbstractBase* p = new Derived; // 可多態
3. 靜態成員獨立性
template class Counter { static int count; };
Counter ci1, ci2; // 共享 count
Counter cd1, cd2; // 另一組 count
// ci1 與 cd1 無共享靜態成員
4. 模板具現化策略
- 隱式具現化:用到時編譯器自動產生 (最常用)
- 顯式具現化:
template class Stack; 在 .cpp 強制產生,減少編譯時間
- 顯式具現化宣告:
extern template class Stack; (C++11) 告知編譯器他處已具現化
泛型程式設計 (GP) 與模板方法模式
- GP 核心:抽象演算法 (如
sort, find_if) 與資料結構分離,透過迭代器、仿函數、型別特徵實現極致複用。
- 模板方法模式 (GoF):基類定義演算法骨架,衍生類實作細節步驟。C++ 可用 CRTP (Curiously Recurring Template Pattern) 實現靜態多態,避免虛函數開銷。
現代 C++ (C++11/14/17/20) 增強