天天看點

C++模闆與泛型程式設計(1.函數模闆與類模闆)

所謂泛型程式設計就是以獨立于任何特定類型的方式編寫代碼,模闆是泛型程式設計的基礎。

1.函數模闆

我們經常會遇到需要編寫函數比較兩個對象大小的情況,比如需要比較兩個int值得大小、兩個char值的大小等等,在C++以前,我們需要為每一種對象編寫對應的函數,甚至必須取不同的函數名,有了C++以後,還沒接觸到泛型程式設計前我們可能會利用函數重載來避免要取多個不同名字的麻煩,但還是需要編寫多個函數:

int compare(const int &a, const int &b)
{
    if(a < b) return -1;
    if(b < a) return 1;
    return 0;
}

int compare(const string &a, const string &b)
{
    if(a < b) return -1;
    if(b < a) return 1;
    return 0;
}
           

這些函數執行的操作都是一模一樣的,重複編寫相同的内容相當低效,有了泛型程式設計的思想後,我們可以定義一個函數模闆,編譯器會自己根據給定的模闆實參生成不同的函數(執行個體):

template <typename T> int compare(const T &a, const T &b)
{
    if(a < b) return -1;
    if(b < a) return 1;
    return 0;
}
           

函數模闆定義從template關鍵字開始,後面括号裡是模闆形參表(template parameter list),模闆形參T可以是表示類型的類型形參(type parameter),也可以是表示常量表達式的非類型形參(nontype parameter),此處的typename也可由class代替,兩者沒有差別。

使用函數模闆時,編譯器會推斷傳入函數compare的形參的類型(模闆實參),并生成對應于此類型的函數(執行個體):

int main()
{
    std::string a("hello"), b("world");
    //生成int compare(const std::string &, const std::string &);
    int ret1 = compare(a, b);

    int m = 10, n = 5;
    //生成int compare(const int &, const int &);
    int ret2 = compare(m, n);

    return 0;
}
           

函數模闆也可以聲明為inline,inline說明符必須放在模闆形參清單之後:

template <typename T> inline compare(const T &, const T &);
           

我們的類中可能會定義一些自己的類型,比如标準庫的很多容器類裡都定義了size_type類型,當我們在函數模闆中使用這樣的類型時需要注意指明這是一個類型而不是一個對象,為此,我們必須在類型名前加上typename關鍵字,若不指定,編譯器将預設這是一個對象:

class Test{
    typedef std::vector<std::string>::size_type line_num;
    ...
};

template <typename T> func(const T &a)
{
    //通過加上typename指明line_num是Test類中的類型而不是一個對象
    //指定後此句就是聲明了一個指針,否則就是在計算對象line_num和p的積
    typename Test::line_num *p;
    ...
}
           

2.類模闆

C++提供了很多類模闆,比如我們經常用到的vector、list、queue等,我們可以使用類模闆來定義自己的類。

類模闆的定義與函數模闆類似,都以template關鍵字接模闆形參表開頭:

template <typename T> class MyClass{
    ...
};
           

我們可以像使用vector、list那樣來使用我們自己的類模闆:

MyClass<int> a;
MyClass< vector<std::string> > b;
MyClass<string> c;
           

可以看出,與函數模闆不同的是,在使用類模闆時必須顯式指定模闆實參,這是因為編譯器可以通過傳入函數的參數類型推斷函數模闆的模闆實參,而類模闆的模闆實參無從推斷,故必須顯式指定。

編寫泛型程式的原則:對實參類型的要求盡可能少

上面的compare函數模闆也展現了這個原則:

  • 函數形參是const引用,這樣就允許不能進行複制操作的類型調用該函數
  • 函數體的測試中隻用到了<,這樣就允許隻支援<而不支援>的類型調用該函數

    有很多C++程式員在循環條件判斷等處習慣使用!=而不是<或>也展現了這樣的原則,因為有很多類型隻支援判斷相等或不等,而不支援大于小于操作。

繼續閱讀