天天看點

C++——模闆特化和偏特化

1.引言

C++中的模闆分為類模闆和函數模闆,雖然它引進到C++标準中的時間不是很長,但是卻得到了廣泛的應用,這一點在STL中有着充分的展現。目前,STL在C++社群中得到了廣泛的關注、應用和研究。了解和掌握模闆是學習、應用和研究以及擴充STL的基礎。而STL模闆執行個體中又充斥着大量的模闆特化和偏特化。

2.模闆的定義

(1) 類模闆

定義一個棧的類模闆,它可以用來容納不同的資料類型

說明如下:

template <class T>

class stack {

private:

  list* top;

public:

  stack();

  stack(const stack&);

  ~stack();

  void push(T&);

  T& pop();

  //…

};

上述定義中,template告訴編譯器這是一個模闆,尖括号中的<class T >指明模闆的參數,可以有一個或多個,具體實作時由使用者指定,其中template <class T >中的關鍵字class可以用關鍵字typename來代替。

類模闆的使用除了要在聲明時指明模闆參數外,其餘均與普通的類相同,例如:

stack<int> int_stack;

stack<char> ch_stack;

stack<string> str_stack;

int_stack.push(10);

ch_stack.push(‘z’);

str_stack.push(“c++”);

(2)函數模闆

假設現在要定義一個max函數來傳回同一類型(這種類型是允許比較的)兩個值的最大者.

template<class T>

T mymax(const T& t1,const T& t2)

{ return t1 < t2 ? t2 : t1; }

template <class T>的意義與類模闆定義中相同。

模闆函數的使用與普通非模闆函數使用相同,因為模闆函數的參數可以從其傳入參數中解析出來。例如:

int highest = mymax(5,10);

char c = mymax(‘a’, ’z’);

mymax(5,10)解析出模闆函數參數為int, mymax(‘a’, ’z’)解析出模闆函數的參數為char。

3.模闆的特化

(1)類模闆特化

有時為了需要,針對特定的類型,需要對模闆進行特化,也就是特殊處理.例如,stack類模闆針對bool類型,因為實際上bool類型隻需要一個二進制位,就可以對其進行存儲,使用一個字或者一個位元組都是浪費存儲空間的.

template <class T>

class stack {};

template < >

class stack<bool> { //…// };

上述定義中template < >告訴編譯器這是一個特化的模闆。

(2) 函數模闆的特化

看下面的例子

main()

{

  int highest = mymax(5,10);

  char c = mymax(‘a’, ’z’);

  const char* p1 = “hello”;

  const char* p2 = “world”;

  const char* p = mymax(p1,p2);

}

前面兩個mymax都能傳回正确的結果.而第三個卻不能,因為,此時mymax直接比較兩個指針p1 和 p2 而不是其指向的内容.

針對這種情況,當mymax函數的參數類型為const char* 時,需要特化。

template <class T>

T mymax(const T t1, const T t2)

{

   return t1 < t2 ? t2 : t1;

}

template <>

const char* mymax(const char* t1,const char* t2)

{

   return (strcmp(t1,t2) < 0) ? t2 : t1;

}

現在mymax(p1,p2)能夠傳回正确的結果了。

4.模闆的偏特化

模闆的偏特化是指需要根據模闆的某些但不是全部的參數進行特化

(1) 類模闆的偏特化

例如c++标準庫中的類vector的定義

template <class T, class Allocator>

class vector { // … // };

template <class Allocator>

class vector<bool, Allocator> { //…//};

這個偏特化的例子中,一個參數被綁定到bool類型,而另一個參數仍未綁定需要由使用者指定。

(2) 函數模闆的偏特化

  嚴格的來說,函數模闆并不支援偏特化,但由于可以對函數進行重載,是以可以達到類似于類模闆偏特化的效果。

  template <class T> void f(T);  (a)

  根據重載規則,對(a)進行重載

  template < class T> void f(T*);  (b)

  如果将(a)稱為基模闆,那麼(b)稱為對基模闆(a)的重載,而非對(a)的偏特化。C++的标準委員會仍在對下一個版本中是否允許函數模闆的偏特化進行讨論。

5.模闆特化時的比對規則

(1) 類模闆的比對規則

最優化的優于次特化的,即模闆參數最精确比對的具有最高的優先權

例子:

template <class T> class vector{//…//}; // (a)  普通型

template <class T> class vector<T*>{//…//};  // (b) 對指針類型特化

template <>   class vector <void*>{//…//};  // (c) 對void*進行特化

每個類型都可以用作普通型(a)的參數,但隻有指針類型才能用作(b)的參數,而隻有void*才能作為(c)的參數

(2) 函數模闆的比對規則

非模闆函數具有最高的優先權。如果不存在比對的非模闆函數的話,那麼最比對的和最特化的函數具有高優先權

例子:

template <class T> void f(T);  // (d)

template <class T> void f(int, T, double); // (e)

template <class T> void f(T*);  // (f)

template <> void f<int> (int) ; // (g)

void f(double);  // (h)

bool b;

int i;

double d;

f(b); // 以 T = bool 調用 (d)

f(i,42,d) // 以 T = int 調用(e)

f(&i) ; // 以 T = int* 調用(f)

f(d);  //  調用(g)我怎麼覺得是調用(h)

參考文獻

[1] Bjarne Stroustrup, The C++ Programming Language (Special Edition), Addison Wesley,2000

[2] Nicolai M.Josuttis, The C++ Standard Library – A Tutorial and Reference ,Addison Wesley,1999

[3] Stanley Lippman and Josée Lajoie ,C++ Primier, 3rd Edition ,Addison Wesley Longman ,1998

補充:

繼續閱讀