函數模板——隱式實例化、顯式實例化、顯式具體化


一、什么是實例化和具體化?

        為進一步了解模板,必須理解術語實例化和具體化。

(1)、實例化:在程序中的函數模板本身並不會生成函數定義,它只是一個用於生成函數定義的方案。編譯器使用模板為特定類型生成函數定義時,得到的是模板實例。這即是函數模板的實例化。而函數模板實例化又分為兩種類型:隱式實例化和顯式實例化

例如:

template < typename T >
void Swap( T &a, T &b )
{
T temp;
temp = a;
a = b;
b = temp;
}
int main(void)
{
int a= 1, b = 2;
Swap(a, b);
Swap<int>(a, b);
return 0;
}

可以發現,在主函數中有兩種Swap函數調用。

第一個Swap(a, b)導致編譯器自動識別參數類型生成一個實例,該實例使用int類型,此為隱式實例化。    

而第二個Swap<int>(a, b),直接命令編譯器創建特定的int類型的函數實例,用<>符號指示類型,此為顯式實例化。

(2)、具體化:即顯式具體化,與實例化不同的是,它也是一個模板定義,但它是對特定類型的模板定義。顯式具體化使用下面兩個等價的聲明之一:

template <> void Swap<int>(int &, int &);
template <> void Swap(int &, int &);

可以發現,顯式具體化聲明在關鍵字template后包含<>。上面聲明的意思是"不要使用Swap()模板來生成函數定義,而應使用專門為int類型顯式地定義的函數的定義"。這些原型必須有自己的函數定義。

在這里,有人可能要說了:”明明可以通過隱式實例化自動生成int類型的函數定義,為何還要弄出一個顯式具體化來弄出另外一個模板呢?這不是多此一舉嗎?”

我要解釋一下,顯式具體化的主要用途!而在介紹用途之前,我們先來了解一下普通函數模板的局限性。

二、模板的局限性

假設有如下模板函數:

template <typename T>
void fun(T a, T b)
{ ... }

通常,代碼假定可執行哪些操作。例如,下面的代碼假定定義了賦值。

a = b;
但是如果T為數組,這種假設將不成立!

同樣,下面的語句假設定義了<

if ( a > b )
但如果T為結構,則該假設便不成立!

另外,為數組名定義了運算符 > ,但由於數組名是常量地址,因此它比較的是數組的地址,而這並不是我們所期望的操作。下面的語句假定為類型T定義了乘法運算符,但如果T為數組、指針或結構,這種假設便不成立:

T c = a * b;

       總之,編寫的模板函數很可能無法處理某些類型。通常在C++中有一種解決方案是:運算符重載,以便能將其用於特定的結構或類。就是說一個類重載了運算符+之后,使用運算符+的模板便可以處理重載了運算符+的結構。

       但是,還有另外一種解決方案:為特定類型提供具體化的模板定義(這就是顯式具體化的主要用途)。

三、顯式具體化

       假定定義了如下結構:

struct job
{
  char name[40];
  double salary;
  int floor;
}

       另外,假設希望能夠交換兩個這種結構的內容。原來的模板使用下面的代碼來完成交換:

    temp = a;
    a = b;
    b = temp;

       由於C++允許將一個結構賦給另一個結構,因此即使T是一個job結構,上述代碼也可適用。然而,如果只想交換salary和floor成員,而不交換name成員,則需要使用不同的處理代碼。但Swap() 的參數將保持不變(兩個job結構的引用),因此無法使用模板的重載來提供其他代碼(模板重載,模板的參數列表必須不同)。這時,就得用顯式具體化來實現這個需求。

       上面已經介紹過顯式具體化的聲明方式,我們直接通過代碼實例來看一下:

#include <iostream>
using namespace std;

//job結構
struct job
{
  char name[40];
  double salary;
  int floor;
};

//普通交換模板
template <typename T>
void Swap(T &a, T &b)
{
  T temp;
  temp = a;
  a = b;
  b = temp;
}

//job類型的顯式具體化模板
template <> void Swap<job>(job &j1, job &j2)
{
  double t1;
  int t2;

  //交換salary
  t1 = j1.salary;
  j1.salary = j2.salary;
  j2.salary = t1;

  //交換floor
  t2 = j1.floor;
  j1.floor = j2.floor;
  j2.floor = t2;
}

int main(void)
{
  int inta = 1, intb = 2;
  job zhangSan = {"張三", 80000, 6};
  job liSi = {"李四", 60000, 4};

  cout << "inta = " << inta << " inta = " << intb << endl;
  cout << zhangSan.name << " , " << zhangSan.salary << " , " << zhangSan.floor <<endl; 
  cout << liSi.name << " , " << liSi.salary << " , " << liSi.floor <<endl; 

  Swap(inta, intb); //編譯器將實例化普通模板的int類型函數

  Swap(zhangSan, liSi); //編譯器將實例化顯式具體化模板job類型函數

  cout << "\n交換后:\n" << endl;
  cout << "inta = " << inta << " inta = " << intb << endl;
  cout << zhangSan.name << " , " << zhangSan.salary << " , " << zhangSan.floor <<endl; 
  cout << liSi.name << " , " << liSi.salary << " , " << liSi.floor <<endl;
  return 0;
}    

 

       

 

程序運行時匹配模板時,遵循的優先級是:

        具體化模板優先於常規模板,而非模板函數優先於具體化和常規模板。

總結:隱式實例化和顯式實例化側重於函數調用,顯式具體化側重於函數定義。

   

 


參考鏈接:https://blog.csdn.net/SunXiWang/article/details/78667444?depth_1-utm_source=distribute.pc_relevant.none-task&utm_source=distribute.pc_relevant.none-task

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM