1. 程式人生 > >函式模板和模板函式

函式模板和模板函式

1.函式模板的宣告和模板函式的生成

1.1函式模板的宣告

函式模板可以用來建立一個通用的函式,以支援多種不同的形參,避免過載函式的函式體重複設計。它的最大特點是把函式使用的資料型別作為引數。

函式模板的宣告形式為:

template<typename 資料型別引數識別符號>

<返回型別><函式名>(引數表)

{

    函式體

}

其中,template是定義模板函式的關鍵字;template後面的尖括號不能省略;typename(或class)是宣告資料型別引數識別符號的關鍵字,用以說明它後面的識別符號是資料型別識別符號。這樣,在以後定義的這個函式中,凡希望根據實引數據型別來確定資料型別的變數,都可以用資料型別引數識別符號來說明,從而使這個變數可以適應不同的資料型別。例如:

template<typename T>

T fuc(T x, int y)

{

    T x;

    //……

}

如果主調函式中有以下語句:

double d;

int a;

fuc(d,a);

則系統將用實參d的資料型別double去代替函式模板中的T生成函式:

double fuc(double x,int y)

{

    double x;

    //……

}

函式模板只是聲明瞭一個函式的描述即模板,不是一個可以直接執行的函式,只有根據實際情況用實參的資料型別代替型別引數識別符號之後,才能產生真正的函式。

關鍵字typename也可以使用關鍵字class,這時資料型別引數識別符號就可以使用所有的C++資料型別。

1.2.模板函式的生成

函式模板的資料型別引數識別符號實際上是一個型別形參,在使用函式模板時,要將這個形參例項化為確定的資料型別。將型別形參例項化的引數稱為模板實參,用模板實參例項化的函式稱為模板函式。模板函式的生成就是將函式模板的型別形參例項化的過程。例如:

使用中應注意的幾個問題:

⑴ 函式模板允許使用多個型別引數,但在template定義部分的每個形參前必須有關鍵字typename或class,即:

template<class 資料型別引數識別符號1,…,class 資料型別引數識別符號n>

<返回型別><函式名>(引數表)

{

     函式體

}

⑵ 在template語句與函式模板定義語句<返回型別>之間不允許有別的語句。如下面的宣告是錯誤的:

template<class T>

int I;

T min(T x,T y)

{

   函式體

}

⑶ 模板函式類似於過載函式,但兩者有很大區別:函式過載時,每個函式體內可以執行不同的動作,但同一個函式模板例項化後的模板函式都必須執行相同的動作。

2 函式模板的異常處理

函式模板中的模板形參可例項化為各種型別,但當例項化模板形參的各模板實參之間不完全一致時,就可能發生錯誤,如:

template<typename T>       

void min(T &x, T &y)

{  return (x<y)?x:y;  }

void func(int i, char j)

{

   min(i, i);

   min(j, j);

   min(i, j);

   min(j, i);

}

例子中的後兩個呼叫是錯誤的,出現錯誤的原因是,在呼叫時,編譯器按最先遇到的實參的型別隱含地生成一個模板函式,並用它對所有模板函式進行一致性檢查,例如對語句

min(i, j);

先遇到的實參i是整型的,編譯器就將模板形參解釋為整型,此後出現的模板實參j不能解釋為整型而產生錯誤,此時沒有隱含的型別轉換功能。解決此種異常的方法有兩種:

⑴採用強制型別轉換,如將語句min(i, j);改寫為min(i,int( j));

⑵用非模板函式過載函式模板

方法有兩種:

① 借用函式模板的函式體

此時只宣告非模板函式的原型,它的函式體借用函式模板的函式體。如改寫上面的例子如下:

template<typename T>       

void min(T &x, T &y)

{  return (x<y)?x:y;  }

int min(int,int);

void func(int i, char j)

{

   min(i, i);

   min(j, j);

   min(i, j);

   min(j, i);

}

執行該程式就不會出錯了,因為過載函式支援資料間的隱式型別轉換。

② 重新定義函式體

就像一般的過載函式一樣,重新定義一個完整的非模板函式,它所帶的引數可以隨意。C++中,函式模板與同名的非模板函式過載時,應遵循下列呼叫原則:

• 尋找一個引數完全匹配的函式,若找到就呼叫它。若引數完全匹配的函式多於一個,則這個呼叫是一個錯誤的呼叫。

• 尋找一個函式模板,若找到就將其例項化生成一個匹配的模板函式並呼叫它。

• 若上面兩條都失敗,則使用函式過載的方法,通過型別轉換產生引數匹配,若找到就呼叫它。

•若上面三條都失敗,還沒有找都匹配的函式,則這個呼叫是一個錯誤的呼叫。

3.類中巢狀模版:

#include <iostream>

using namespace std;

 class X
{

   template <class T>
   struct Y
   {
      T m_t;
      Y(T t): m_t(t) { }   
   };

   Y<int> yInt;
   Y<char> yChar;

public:
   X(int i, char c) : yInt(i), yChar(c) { }
   void print()
   {
      cout << yInt.m_t << " " << yChar.m_t << endl;
   }
};

int main()
{
   X x(1, 'a');
   x.print();
}
//當類中巢狀模版時,要通過外部類的建構函式將引數傳遞給內部類,將其與內部類相聯絡起來
//內部類要通過具體的型別來例項化內部類,生成一個具體的物件這樣才能在內部使用此類模版帶來的
//具體的類的好處。這樣就可以在內部使用此類了,對外隱藏了此類內部還有這些個功能。
#include <iostream>
using namespace std;
template <class T>
class X
{
template <class U>
class Y
{
U* u;
public:
Y();
U& Value();
void print();
~Y();
};
Y<int> y;
public:
X(T t) { y.Value() = t; }
void print() { y.print(); }
};
template <class T>template <class U>
X<T>::Y<U>::Y()
{
cout << "X<T>::Y<U>::Y()" << endl;
u = new U();
}
template <class T>template <class U>
U& X<T>::Y<U>::Value()
{
return *u;
}


template <class T>template <class U>
void X<T>::Y<U>::print()
{
cout << this->Value() << endl;
}
template <class T>template <class U>
X<T>::Y<U>::~Y()
{
cout << "X<T>::Y<U>::~Y()" << endl;
delete u;
}
int main()
{
X<int>* xi = new X<int>(10);
X<char>* xc = new X<char>('c');
xi->print();
xc->print();
delete xi;
delete xc;
}
當巢狀類模板在其封閉類的外部定義時,它們必須以類模板(如果它們是類模板的成員)的模板引數和成員模板的模板引數開頭。

要以其模版引數開頭,由外部向內部開始宣告模版引數

template <class T>template <class U>

void X<T>::Y<U>::print()
     {
cout << this->Value() << endl;
      }
要從外部開始寫起,寫清楚作用域,且不能引起歧義。