最近更新: 2007-07-11

C++ Template 筆記

C++ 的 Template 是種將資料型態參數化的功能。將資料型態資訊自程式碼中抽離,代之以簡化的符號 (T, T1, T2, ...)。再由編譯器透過類似巨集代換的方式,根據樣板內容產生實際的程式碼。

  • Function Template (函數樣板)
  • Class Template (類別樣板)。含部份特殊化。

Function Template (函數樣板)

以關鍵字 template 起始一個樣板宣告,後接參數。C/C++ 的程式語法,是以 ( ) 括起參數。而 Template 語法,則以 < > 括起參數。樣板所用參數之完整稱呼是「樣板參數(template parameters)」,慣例以 T, T1, T2 等作為樣板參數名稱。接著再寫上函數的程式碼樣板,又稱原型 (prototype)。函數原型的函數名稱即為函數樣板的名稱。

template < 樣板參數型態 樣板參數名 [, 其他樣板參數] >
原型回傳型態 函數名(參數型態 原型參數名, ...) {
    //prototype codes;
}

參數型態可用關鍵字 classtypename 表示泛用型態 (即任何型態);或是一個已宣告的資料型態,如 int 與自定類。原型中的參數型態若是已宣告的資料型態,則是一種特殊化的函數樣板。

//normal function:
int myAdd(int a, int b) {
    return a + b;
}

//function template
template <class T> T myAdd(T a, T b)
{
    return a + b;
}

//基於C/C++的自由撰寫風格,通常分兩行。
template <class T>
T myAdd(T a, T b)
{
    return a + b;
}

//apply:
int a = 1, b = 2;
myAdd(a, b);

題外話。多數動態語言並不需要樣板,它們原本就具有泛型處理的語意與概念。

//In JavaScript

function myAdd(a, b) {
    return a + b;
}
var a = 1, b = 2;
myAdd(a, b);

Class Template (類別樣板)

語法與函數樣板相同,差別在其原型為類別。此外,樣板參數可以設定預設值。

套用類別樣板的語法則是以樣板名稱括起參數值,括號用 < >。如: templateName<argument>

//normal class:
class myClass {
    myClass& add(const myClass& a) {
        return *this;
    }
};

//class template:
template <class T>
class myTClass
{
    T& add(const T& a) {
        return *this;
    }
};

int main() {
    myTClass<int>  a, b;
    myTClass<double> x,y;
}
部份特殊化 (partial specialization)

套用一個通用型的樣板以宣告特殊情形的樣板,特殊情形的樣板參數指明採用已宣告之資料型態。對編譯器而言,特殊化樣板是添加在通用型樣板之下的特殊適用條例,並不是一個新樣板。編譯器視實際的參數型態決定援引一般條例或特殊條例,不需程序員指示。

//通用型樣板、泛型樣板:
template <class T1, class T2> class myTClass;

//部份特殊化:
template <class T> class myTClass<T, int>;
                   //-------------------- 套用 myTClass 樣板,引數為 T 和 int
                   //亦即第二個樣板參數之值指明為 int 。
//----------------- 宣告一個 myTClass 的部份特殊化樣板

// general template class
template <class T1, class T2>
class myTClass
{
public:
    T1& add(const T2& a) {
        return *this;
    }
};

// 部份特殊化
template <class T>
class myTClass<T, int>
{
public:
    T& add(const int& a) {
        return *this;
    }
};

int main() {
    myTClass<int, int>  a; //援引特殊化樣板(第2個參數為int型態之特殊情形)
    myTClass<int, double> b; //援引泛型樣板
    return 0;
}
原型之函數成員特殊化

原型的方法(函數成員)特殊化。特殊化的原型方法不可以寫在類別樣板的宣告中。語法是以套用類別樣式之敘述代替函數成員的類別範圍,且樣板參數值須為已宣告之資料型態。

template <class T>
class myTClass
{
public:
    void display(T b) {
        cout << b << std::endl;
    }
};

void myTClass<int>::display(int b) {
     //----------- 特化 myTClass 樣板之原型函數成員,樣板參數值為 int
    cout << std::setw(10) << b << endl;
}
特殊類別的樣板

當特殊化樣板之樣板參數列為空值,亦即不接受任何型態參數時,此特殊化樣板是一個被當成樣板的類別。

當樣板參數列為空值時,則原型中出現的資料型態都必須指明為已宣告的資料型態。此時程序員的工作與設計一個傳統類別 (non-template class) 無異。差別僅在編譯器將此類別視為一個樣板,故使用時不需要區分兩者。

#include <iostream>
#include <iomanip>
#include <string>

template <class T>
class Set
{
    T t;
public:
    Set(T st) : t(st) {}

    void display() {
        std::cout << t << std::endl;
    }
};

// Normal class, Not-template class
class Set_int
{
    int t;
public:
    Set_int(int st): t(st) {}

    void display() {
        std::cout << std::setw(10) << std::setfill('0')
            << t << std::endl;
    }
};

// Specialized class template
template <> class Set<int>
{
    int t;
public:
    Set(int st): t(st) {}

    void display() {
        std::cout << std::setw(10) << std::setfill('o')
            << t << std::endl;
    }
};

int main() {
    Set<std::string> a("hello");
    Set_int b(2);  // User needs to know there is a non-template class.
    Set<int> c(2); // User doesn't need to know there is a non-template class.

    a.display();
    b.display();
    c.display();
    return 0;
}
樂多舊網址: http://blog.roodo.com/rocksaying/archives/3641717.html