模板的特化和偏特化以及相關理解
寫在前面:
主要是對於模板的特化和偏特化做一個總結以及個人的理解。
為什麼需要特化或者偏特化:
模板為什麼要特化,因為編譯器認為,對於特定的型別,如果你能對某一功能更好的實現,那麼就該聽你的。
對於一般的類模板,模板引數T是在編譯的時候編譯器根據你傳入的引數自動生成對應的程式碼,這樣的好處是顯而易見的對於同樣的處理或者同樣的操作過程可以很好的封裝在一個模板當中,根據傳入的不同的引數具體實現對於不同的資料或者物件的操作。
但是這樣也是有些問題是解決不了的,當有些資料型別或者物件不支援和其他的資料型別或者物件相同的操作的時候,就需要用其他的方式去實現同樣一個過程,這個時候僅僅改變一個模板引數T是遠遠不行的。
而特化和偏特化其實就應對這樣的情況。
對於某些特殊的資料型別,需要不同的實現方式的時候,就可以採用這樣的方式。
舉例
下面是類模板,全特化和偏特化的一個簡單的例子:
template<typename T1, typename T2>
class Test
{
public:
Test(T1 i,T2 j):a(i),b(j){cout<<"模板類"<<endl;}
private:
T1 a;
T2 b;
};
template<>
class Test<int , char>
{
public:
Test(int i, char j):a(i),b(j){cout<<"全特化"<<endl;}
private:
int a;
char b;
};
template <typename T2>
class Test<char, T2>
{
public:
Test(char i, T2 j):a(i),b(j){cout<<"偏特化"<<endl;}
private:
char a;
T2 b;
};
Test<double , double> t1(0.1,0.2);
Test<int , char> t2(1,'A');
Test<char, bool> t3('A',true);
可以看出全特化就是將模板的引數寫死,針對這樣一種具體的實現,告訴編譯器要去生成的是這段程式碼,而非是將引數傳入模板當種簡單的替換。
偏特化就屬於兩者之間,有某些引數被寫定,有些引數還是T根據例項化的時候去替換。
注意:而對於函式模板,卻只有全特化,不能偏特化。
總結:
1.對於不同的資料型別要採用同樣的形式進行封裝(泛型),可以採用這樣的方式,這樣一個模板就可以處理多樣的資料,對於用這個模板的人來說就很方便和簡單。
2.在模板宣告定義的下面接著類似的宣告和定義全特化和偏特化的模板就可實現這樣的功能。
3.對於類模板定義和實現只能放在同一個.cpp檔案當中,不要將定義放在.h檔案具體的實現放在.cpp當中,這和一般的類不同。原因如下:一般的類來說,可能會被多個檔案include將所有的東西寫在標頭檔案裡就會被多次引入,這樣會使得程式碼過於龐大,並且一旦這個類的某個實現改動將導致所有引入這個標頭檔案的檔案需要重新編譯,增大了其他檔案對於這個類的依賴性,對於非常大的工程來說這是極其的耗時的。這是一般的類為什麼需要分.h和.cpp的原因。而類模板截然不同,因為模板類對於程式實際是不存在的,僅僅是給編譯器看的,告訴編譯器當出現這樣的情況時可以生成怎樣的程式碼。因此只需要一份即可,如果分開寫,被include的時候只看得到定義看不到具體的實現還是沒有用,因為.cpp檔案不會被include所以編譯器看不到。
4.類模板的靜態成員變數近屬於例項化後的類,不同的模板例項不共享同一個靜態成員變數。
模板函式的問題
下面總結一些關於模板函式的理解。
template <class T>
void sort(T element[], unsigned int count)
{
//具體的實現
}
那麼對於傳入的具體的element[]模板函式是怎麼作用的呢?
首先,模板函式其實就是定義了一系列的過載的函式,要使用這些函式需要先例項化。
例項化分為隱式的和顯示的。一般是隱式的。編譯器會根據呼叫時的引數自動的將函式模板例項化為具體的函式。這個過程叫做模板實引數推演。
當編譯器無法從傳入的引數來確定多呼叫的模板函式的時候,就需要顯示的例項化模板函式。
sort<int>(a,100);