首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> C++ >> C++ 入門

C++ 成員模板完整指南:定義、實例化與標準庫應用

     編輯:C++ 入門

什麼是成員模板?

成員模板 是類別 (無論是否為類別模板) 內部宣告的模板成員函式或巢狀類別模板。它們允許類別以泛型方式接受多種型別的參數,是 C++ 泛型程式設計的核心機制之一。標準庫中大量使用:std::vector 的迭代器建構子、`assign`、`insert`;std::shared_ptr 的轉換建構子等。

1. 宣告與定義語法

類別內宣告

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。

2. 實例化機制:兩階段推導

階段一:類別模板實例化 (由物件型別決定)

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)。

關鍵點

  • 成員模板 只有在被使用時才實例化 (隱式實例化)。
  • 外層參數由物件型別鎖定,內層參數由呼叫實參推導,兩階段獨立。
  • 若成員模板從未被呼叫,永不實例化,甚至允許定義中有錯誤 (但編譯器可能仍檢查語法)。

3. 標準庫實戰範例

vector 範圍建構子與 assign

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) 等跨容器複製成為可能。

shared_ptr 轉換建構子

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>() 等多態轉換。

4. 進階技巧與避坑

SFINAE 限制模板參數 (C++11/20)

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);

完美轉發與通用引用 (C++11)

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)

非模板函式在多載解析中優先於模板,這通常是期望行為。

5. 總結

特性說明
宣告位置類別內部 (模板或非模板類別皆可)
模板參數獨立於外層類別模板參數
虛函式不可為虛函式
定義語法雙重 template:外層參數在前,內層在後
實例化時機使用時 (隱式),兩階段推導
標準庫典型用途範圍建構子、assign、insert、轉換建構子、比較函子

掌握成員模板,是編寫泛型容器、智慧指標、演算法框架的關鍵技能。它將「演算法邏輯」與「資料結構/迭代器型別」解耦,實現了 C++ 泛型程式設計的核心哲學:對型別參數化,對演算法抽象化。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved