1. 程式人生 > >C++PrimerPlus學習之函式模板

C++PrimerPlus學習之函式模板

函式模板的宣告

template <typename T>//typename 也可以用class替換
void Swap(T &a,T &b)
{
    T tmp=a;
    a=b;
    b=tmp;
}
int main()
{
    int a=3,b=4;
    Swap(a,b);
    cout<<a<<' '<<b<<endl;
}

有時編寫的模板函式很可能無法處理某些型別,這時候我們可以為特定型別提供具體化的模板定義。

顯示具體化

  • 對於給定的函式名,可以有非模板函式,模板函式和顯示具體化模板函式以及它們的過載版本。
  • 顯示具體化的原型和定義應以template<>打頭,並通過名稱來指出型別。
  • 非模板版本優先於顯示具體化,顯示具體化優先於模板版本(非模板版本>顯示具體化>模板版本)。

下面是用於交換job結構的非模板函式,模板函式和具體化的原型

struct job
{
    char name[40];
    double salary;
    int floor;
};
//非模板函式
void Swap(job &,job &);
//模板函式
template <typename T>
void Swap(T &,T &
); //顯示具體化 template<> void Swap<job>(job &,job &);

例項化和具體化的區別

  • 在程式碼中包含函式模板並不會生成函式定義,它只是一個用於生成函式定義的方案。
  • 在呼叫模板函式時,編譯器自動生成函式定義,得到模板例項 – 隱式例項化。
  • 顯式例項化 – 前加template關鍵字 (不加尖括號)
    template void swap_diy<char>(char&, char&); // 只需要宣告即可
    
  • 此宣告令編譯器使用模板生成一個使用char型別的函式定義 – 即生成模板例項
  • 不能在同一個檔案中同時使用同一種類型的顯式具體化和顯式例項化
  • 也可在函式呼叫時顯式指定型別
    swap_diy<double>(a, b);
    

過載解析

步驟

  • 建立候選函式列表。其中包含與被呼叫函式的名稱相同的函式和模板函式。

  • 使用候選函式列表建立可行函式列表。這些都是引數數目正確的函式,為此有一個隱式轉換序列,其中包括實參型別與相應的形參型別完全匹配的情況。

  • 確定是否有最佳的可行函式。

    • 完全匹配,但常規函式優先於模板。
    • 提升轉換(如,char和shorts自動轉換為int,float自動轉換為double)。
    • 標準轉換(例如,int轉換為char,long轉換為double)。
    • 使用者定義的轉換,如類宣告中定義的轉換。

如果出現多個完全匹配的函式,編譯器將會生成一條錯誤訊息-“二義性”。但有時候,即使兩個函式都完全匹配,仍可完成過載解析。首先指向非const資料的指標和引用優先於const指標和引用引數匹配。(注意只適用於指標和引用)。
另一個完全匹配優於另一個的情況是,其中一個是非模板函式,而另一個不是。這種情況下,非模板函式將優先於模板函式(包括顯示具體化)。
如果兩個完全匹配的函式都是模板函式,則較具體的模板函式優先。這意味著顯示具體化將優於使用模板隱式生成的具體化。

關鍵字decltype(C++11)

  • 為了解決某些型別是由模板(或其組合)決定,而難以表示的問題

    template<typename T1, typename T2>
    void fun(T1& a, T2& b) {
        some_type apb = a + b;
    }
    
  • decltypevar 使變數var與expression的型別一樣。

    template<typename T1, typename T2>
    void fun(T1& a, T2& b) {
        decltype(a+b) apb = a + b;
    }
    
  • 如果expression是一個沒有用括號括起來的識別符號,則var的型別與該識別符號型別相同,包括const等限定符:

    const int* x;
    decltype(x) w;  // w的型別為const int*
    
  • 如果expression是一個函式呼叫,則var的型別與函式的返回型別相同。

    long fun(int);
    decltype(fun(3)) m; // m的型別為long
    
  • 如果expression是一個左值,則var為指向其型別的引用(必須加括號)

    double x = 3.3;
    decltype((x)) r = x;        // r為double&型別
    decltype(x) v = x;          // v為double型別
    
  • 如果前面的條件都不滿足,則var的型別與expression的型別相同。

  • 若需要多次宣告此型別,可結合typedef

    template<class T1, class T2>
    void fun(T1 x, T2 y) {
        typedef decltype(x + y) xpytype; // 將其定義為另一種型別名
        xpytype xpy = x + y; 
        xpytype arr[10];
        xpytype& rxy = arr[2];
    }
    

後置返回型別(C++11)

template<class T1,class T2>
some_type gt(T1 x,T2 y)
{
	return x+y;
}
  • 此時還沒有宣告x和y,所以不能用decltype。

  • 對於下面的原型

    double h(int x,float y);
    //可以寫成
    auto h(int x,float y)->double;
    
  • 這將返回型別移到引數聲明後面。->double被稱為後置返回型別。其中auto是一個佔位符,表示後置返回型別提供的型別。所以上面的模板函式可寫成

    template<class T1,class T2>
    auto gt(T1 x,T2 y)->decltype(x+y)
    {
    	return x+y;
    }