成員模板 是類別 (無論是否為類別模板) 內部宣告的模板成員函式或巢狀類別模板。它們允許類別以泛型方式接受多種型別的參數,是 C++ 泛型程式設計的核心機制之一。標準庫中大量使用:std::vector 的迭代器建構子、`assign`、`insert`;std::shared_ptr 的轉換建構子等。
template <typename T>
class Queue {
public:
// 成員函式模板:接受任意輸入迭代器
template <typename Iter>
Queue(Iter beg, Iter end) { copy_elems(beg, end); }
template <typename Iter>
void assign(Iter beg, Iter end);
private:
template <typename Iter>
void copy_elems(Iter beg, Iter end);
};
template <typename Iter>),獨立於外層類別模板參數 (T)。// 類別模板參數表 在前,成員模板參數表 在後
template <typename T> // 外層類別模板參數
template <typename Iter> // 成員模板參數
void Queue<T>::assign(Iter beg, Iter end) {
// destroy(); // 假設有銷毀邏輯
copy_elems(beg, end);
}
template <typename T>
template <typename Iter>
void Queue<T>::copy_elems(Iter beg, Iter end) {
while (beg != end) {
push(*beg);
++beg;
}
}
口訣:定義時先寫類別模板參數,再寫成員模板參數,中間不加分號,直接連寫兩行 template。
short a[4] = {0, 3, 6, 9};
Queue<int> qi(a, a+4); // 1. 推導 Queue<int> 的建構子模板參數 Iter = short*
Queue<int> 固定外層參數 T = int。a, a+4 型別為 short*,推導 Iter = short*。Queue<int>::Queue(short*, short*)。vector<int> vi(a, a+4);
qi.assign(vi.begin(), vi.end()); // 2. 推導 assign 成員模板參數 Iter = vector<int>::iterator
Queue<int>。vi.begin() 型別為 vector<int>::iterator,推導 Iter = vector<int>::iterator。void Queue<int>::assign(vector<int>::iterator, vector<int>::iterator)。template <typename T, typename Alloc = allocator<T>>
class vector {
public:
// 範圍建構子
template <typename InputIt>
vector(InputIt first, InputIt last, const Alloc& alloc = Alloc());
// 範圍賦值
template <typename InputIt>
void assign(InputIt first, InputIt last);
// ...
};
使得 vector<int> v(list.begin(), list.end())、v.assign(arr, arr+n) 等跨容器複製成為可能。
template <typename T>
class shared_ptr {
public:
template <typename Y>
shared_ptr(const shared_ptr<Y>& r) noexcept; // Y* 隱式轉換為 T*
// ...
};
支援 shared_ptr<Base> p = make_shared<Derived>() 等多態轉換。
template <typename Iter,
typename = enable_if_t<is_input_iterator_v<Iter>>>
void assign(Iter first, Iter last); // 僅接受輸入迭代器
C++20 Concepts 更優雅:
template <input_iterator Iter>
void assign(Iter first, Iter last);
template <typename T>
class Wrapper {
T value;
public:
template <typename U>
Wrapper(U&& u) : value(std::forward<U>(u)) {} // 通用引用建構子
};
注意:這會抑制隱式複製/移動建構子,需額外聲明 Wrapper(const Wrapper&) = default; 等。
struct A {
template <typename T> void f(T); // #1 成員模板
void f(int); // #2 非模板
};
A a; a.f(10); // 呼叫 #2 (非模板優先)
a.f(3.14); // 呼叫 #1 (T=double)
非模板函式在多載解析中優先於模板,這通常是期望行為。
| 特性 | 說明 |
|---|---|
| 宣告位置 | 類別內部 (模板或非模板類別皆可) |
| 模板參數 | 獨立於外層類別模板參數 |
| 虛函式 | 不可為虛函式 |
| 定義語法 | 雙重 template:外層參數在前,內層在後 |
| 實例化時機 | 使用時 (隱式),兩階段推導 |
| 標準庫典型用途 | 範圍建構子、assign、insert、轉換建構子、比較函子 |
掌握成員模板,是編寫泛型容器、智慧指標、演算法框架的關鍵技能。它將「演算法邏輯」與「資料結構/迭代器型別」解耦,實現了 C++ 泛型程式設計的核心哲學:對型別參數化,對演算法抽象化。