STL源碼剖析(四)模板的特化
在STL中,有許多地方使用到了模板的特化,在往下面講解前,我們需要來學習以下什麼是模板的特化,以及爲什麼需要模板的特殊還有其語法
一、什麼是模板的特化?
首先看一個例子
template <class T>
class A
{
public:
A()
{
std::cout<<"T construct"<<std::endl;
}
};
上述是一個泛化的模板,它支持任何類型
再來看看下面這一個類
template <>
class A<int>
{
public:
A()
{
std::cout<<"int construct"<<std::endl;
}
};
上述類是對類A的一個特化版本,它僅僅支持int,它是在泛化的模板中的一種具體的特化
你可以嘗試以下運行下面這段代碼
int main(int argc, char* argv[])
{
A<char> a1; //T construct
A<int> a2; //int construct
return 0;
}
你會發現,第一類指定了char類型,它使用的是泛化版本的類模板,第二個類指定爲int類型,它使用了特化爲int類型的類模板
是的,這就是模板的特化,C++對模板的編譯規則是,會先查詢是否有符合當前類型的特殊版本,如果沒有,再使用泛化的版本
就像上面,我們沒有定義char類型的特化版本,所以A<char> a1
使用了泛化的類模板,而我們定義了int類型的特化版本,所以A<int> a2
使用了特化爲int類型的類模板
二、爲什麼需要模板的特化?
弄明白了什麼是模板的特化後,我們再來討論爲什麼需要模板的特化
首先我們來看一個例子
如果我們使用泛化的模板,那麼使用這個模板的變量必須符合模板中定義的種種規則
如下面
template <class T>
void func(const T& t)
{
t->print();
}
上述是一個泛化的模板函數,我們的func
的目的是要調用對象的print
函數來打印這個對象,所以要求所有使用這個模板函數的變量都需要定義有print
這個函數
但是事實哪有這麼美好,如果是自定義對象,那麼我們可以很方便的實現print
函數,但如果是對於int
、char
這些原生類型的變量,它們就無法定義自己的print
函數,因此也就無法使用func
了
但是如果我一定要使用func來支持所有類型呢?這個時候,模板特化的作用就體現出來了,我們可以定義int
、char
等類型的特化版本,如下所示
void func(int value)
{
std::cout<<value;
}
void func(char vallue)
{
std::cout<<c;
}
到這裏你應該體會到了特化的作用了吧
模板的特化作用:在一個泛化的模板中,某些類型不適用此泛化的模板函數,或者對於此方法有更優解,那麼這個時候可以特化一個模板來爲其服務
三、模板特化的語法
模板的特化還是挺靈活的,我將其分爲類模板的特化還有函數模板的特化,並舉一些例子,通過這些例子,你就基本可以掌握模板的特化語法
所謂的偏特化與全特化只不過是有沒有將所有的模板參數特化,這裏就不詳細討論了
3.1 類模板的特化
/* 泛化 */
template <class T1, class T2>
class B
{
};
/* 全特化,int類型 */
template<>
class B<int, int>
{
};
/* 偏特化,所有的指針類型 */
template <class T1, class T2>
class B<T1*, T2*>
{
};
/* 偏特化,所有的常量指針類型 */
template <class T1, class T2>
class B<const T1*, const T2*>
{
};
/* 偏特化,只對一個參數進行特化,第二個類型爲char */
template <class T>
class B<T, char>
{
};
3.2 函數模板的特化
/* 泛化 */
template <class T1, class T2>
void func(T1, T2)
{
}
/* 全特化 */
void func(int, int)
{
}
/* 指針偏特化,所有的指針類型 */
template <class T1, class T2>
void func(T1*, T2*)
{
}
/* 指針偏特化,所有的常量指針類型 */
template <class T1, class T2>
void func(const T1*, const T2*)
{
}
/* 偏特化,只對一個模板進行特化,第二個類型爲int */
template <class T>
void func(T, int)
{
}