# C++ template常用功能
  • 函式範本(Function template)又稱「通用函式」(Generic function),它可以適用於不同資料型態的參數列,但實作內容相同的函式.

  • 在template中可以使用關鍵字class或typename,兩者功能相同只是名稱不同而已。

    • Scott Myers, Effective C++ (3rd ed.) item 42中建議如果類別是class時,使用關鍵字class,如果是原生類別如int, float等,使用typename。
template <typename  T>
T max (const T& a, const T& b) {
    return (a>b)? a : b;
}

不可將template的宣告和實作放在不同檔案

/*** test.h ***/
template <class T>
T MaxTest(T x, T y);

/*** test.cpp ***/
template <typename  T>
T MaxTest (const T& a, const T& b) {
    return (a>b)? a : b;
}

/ *** main.cpp ***/
#include <iostream>
#include "test.h"
using namespace std;

int main(){
    cout<<MaxTest(3, 4)<<endl;
    return 0;
}


// compile
g++ -o test main.cpp

// 會出現以下錯誤訊息
main.cpp:(.text+0xf): 未定義參考到「int MaxTest<int>(int, int)」
collect2: error: ld returned 1 exit status
  • 首先,C++中一個編譯單元(translation unit)是指一個 .cpp 以及它所 include 的所有標頭檔 .h 文件,標頭檔裡的所有程式碼會被擴展到包含它的 cpp 檔裡,然後編譯器編譯該 cpp 檔成一個 obj 檔案。

    • obj 是二進位碼,但是不一定能夠執行,因為不能保證裡面有 main 函式。
    • 當編譯器把各別的 cpp 檔都編譯成 obj 檔後,再由連結器(linker)連結成一個執行檔(.exe)。
  • 對於非template的函式MaxTest簡單來說,編譯 main.cpp 時,編譯器不知道 MaxTest 函式的實作程式碼在哪裡,所以遇到 MaxTest 函式時只是先給個指示,指示連結器當找到 MaxTest 實作程式碼實再來取代。

    • 編譯 test.cpp 時,編譯器找到 MaxTest 函式的實作程式碼,並編在 test.obj 裡。
    • 連結時,就把剛剛 main.obj 裡懸而未決的位址換成 MaxTest 實作程式碼的真正位址。
  • 但是 template 比較特別,是在呼叫 template function 時,C++ 編譯器依據引數的資料型態,「自動」產生出所需的函式,這個過程稱為實例化(instantiate)。

    • 在main.cpp中呼叫的是整數類別的int MaxTest(int x, int y),因為它不在 test.h 裡,只好希望連結器可以在別的 obj 裡找到。
    • 但是 test.cpp 裡的是T MaxTest(T x, T y),而不是 int MaxTest(int x, int y),所以根本就沒有 int MaxTest(int x, int y) 的實作程式碼,因此連結錯誤。
  • 關鍵在於 template 只有在需要時才會被實例化出來,所以當編譯器只看到 template 的宣告時,他不能實例化該 template,只能先建立一個外部連結。然而當實現該 template 的 cpp 沒有用到實例化時,編譯器就無法實例化,所以連結失敗。

解決template實例化的問題

  1. 把實作程式碼寫在 .h 檔裡
  2. 在 main.cpp 裡 include "test.cpp"
    • 可以不用把 include "test.cpp" 寫在 main.cpp 裡,那就是寫在 test.h 裡,要特別注意多重 include 的問題。
#ifndef TEST_H
#define TEST_H

template <class T>
T MaxTest(T x, T y);

#include "test.cpp"
#endif

樣板特製化(template Specialization)

  • 同樣名稱的樣板函式或類別可以重複出現,只要針對某個type進行特製化這是為了有時候我們需要對某些Type作"特別"的處理。
template <class T>
void swaps(T& a1,T& a2);

// 特製化樣板 如果傳近來int就什麼都不做
template <>
void swaps<int>(int& a1,int& a2){}

多樣別或帶參數樣板

//帶一個參數
template<class T,int Size>
void print(T t) { cout<<t<<":"<<Size<<endl; }


// 兩個class
template<class T, class U>
void compare(T t, U u){...}

樣版類別(tempalte class)

  • 在開頭宣告的template其有效範圍為整個class
  • template是不能分開成.h和.cpp,如果要把實作分開也要全部再.h檔裏面完成或者用include方式。
// printer.h

template <class T>
class Printer {
    T _t;
public:
 Printer(T t){_t=t;}
 void print(){ cout<<_t<<endl;}

};


// printer.cpp
template <class T>
Printer<T>::Printer(T t){
 _t=t;
}

template <class T>
void Printer<T>::print(){
 cout<<_t<<endl;
}

results matching ""

    No results matching ""