banner
Matrix

Matrix

Abyss
email
github

Petty C++ 模板

模板是 C++ 泛型的一種強大特性,可以應用於類、函數、變量,模板的存在使得 coder 可以編寫類型無關的代碼,而在編譯時生成一般的類、函數和變量。

模板基本形式#

模板定義了一種藍圖,以生成任何可以操作任何類型的數據的類和函數:

模板的基本形式#

以常見的函數模板為例,一個模板的定義如下:

template <typename T>

T func(T param){
  // do something...
  return param;
}

上述代碼是一個具有單個類型參數 T 的函數模板,其參數、返回值都是 T 類型。

  • 關鍵詞template指明定義模板
  • 關鍵詞typename是參數類型的佔位符,T作為模板參數,編譯時,編譯器根據函數的調用將T的實例的類型替換為具體的參數類型,這個過程即為 “模板實例化”。

在調用時,以正常函數的方式調用:

int i = func(42);

類模板#

template <typename T>
class Array {
 private:
   T* data;
   size_t size;
 public:
   explicit Array(size_t size): size(size), data(new T[size]) {}
   ~Array() {delete[] data;}
  
   T& operator[](size_t index) {return data[index];}
}

函數模板#

template <typename T>
T max(T a, T b) {
  return (a > b)? a:b;
}

變量模板#

變量模板是 C++14 引入的新特性,可以生成任何類型的靜態變量:

template <typename T>
constexpr T pi = T(3.1415)

模板的類型參數#

模板對參數數量不限制,這與普通函數一致:

template <typename T, typename U, typename V>

可以定義任意數量的類型參數的模板:

template<typename... Args> class vtclass;

template<typename... Args> 
void func(Arguments... args)

在 C++17 後,可以使用自動推導的類型,當auto用在模板參數位置時,它允許模板接受任何類型的非類型模板參數(non-type template parameter),這包括但不限於整數、字符和布爾值。這樣的模板被稱為自動類型推導的非類型模板參數(auto-typed non-type template parameters):

template <auto x> constexpr auto constant = x;

模板參數<auto x>表示constant可以接受任何類型的常量表達式作為參數。constant的值將會是傳給它的參數x的值。

默認模板變量#

默認模板變量的聲明和普通函數一致:

template <typename T = int>
void func(T factor){
  // do something
}

func<>(42);

模板的特化#

模板提供了很強大的泛型能力,但我們在定義一個模板時,不會為每一種 case 都編寫重複的代碼,很多情況下,我們都希望的是僅當參數類型在某些情況下有著不同的執行路徑,這時候需要用到模板特化。

template <typename T, typename U>

class MyClass{}; // 定義模板

template <typename T>
class MyClass<T, int>{} // 模板特化

MyClass<int, string> myClass1; // 原始的模板

MyClass<int, int> myClass2; // 模板特化

類模板#

類模板的成員函數#

可以在類模板中定義函數,也可以在類的外部進行定義,當模板成員函數在類的外部定義時,需要前置類模板的模板聲明部分來指定。

template<class T, int i>
class MyStack {
    // 類成員和方法聲明
};

template<class T, int i>
void MyStack<T, i>::push(const T item) {
    // do something
}

template<class T, int i>
T& MyStack<T, i>::pop() {
    // do something
}

更常見的做法是在類模板中直接定義模板成員函數,這種寫法有助於編譯器進行內聯處理,提高效率,同樣的也能簡化代碼。

對於一般的非模板的函數或者類,通常的做法是在頭文件中聲明,在源文件中實現,但在模板編程時,為了保證模板在編譯時能夠訪問到完整的模板定義,把模板的定義和聲明都直接放在頭文件中反而是最佳的實踐,否則,除非為每一種類型使用的模板手動實例化,否則在進行鏈接時可能因為找不到模板實例的定義出現鏈接錯誤。

對於複雜大型的模板庫,經常會把模板的實現代碼放在.ipp 或者 .tpp文件中,然後在頭文件底部包含:

// MyTemplate.h
template<typename T>
class MyTemplate {
  public:
    void func(T value);
};

#include "MyTemplate.tpp"
// MyTemplate.tpp, 實際包含模板的實現
template<typename T>
void MyTemplate<T>::doSomething(T value) {
    // 方法實現
}

嵌套類模板#

模板可以進行嵌套:

#include <iostream>

template<class T>
class A{
public:
    template <class U> 
    class B{
    private:
        U u; // 使用U類型的值而不是指針,簡化示例
    public:
        B() {} // 構造函數實現
        U& Value() { return u; } // 返回U類型的引用
        void print() { std::cout << u << std::endl; } // 打印U類型的值
        ~B() {} // 析構函數實現
    };

    B<int> b;

public:
    A(T t) { b.Value() = t; }
    void print() { b.print(); } // 調用B<int>的print
};

// 在外部定義嵌套類模板成員函數時的語法示例
template<class T>
template<class U>
A<T>::B<U>::B() {
    // 構造函數具體實現
}

template<class T>
template<class U>
U& A<T>::B<U>::Value() {
    // 這個函數可以返回U類型的引用,此處只是一個示例
    return u;
}

int main() {
    A<int> a(42); // 創建A類的實例
    a.print(); // 打印42
}

函數模板#

函數模板實例化#

可以使用顯式指定或推導的方式對函數進行實例化:

template<class T> void f(T) { }

template void f<int> (int);

template void f(char);

可以使用extern關鍵字來避免重複的代碼生成(實例化)達到對代碼進行精細控制的目的。


template<typename T, std::size_t N>
class MyClass {};

exterm template MyClass<int, 4>::MyClass(void);
載入中......
此文章數據所有權由區塊鏈加密技術和智能合約保障僅歸創作者所有。