條款41:了解隐士接口和編譯期多态
Template及泛型程式設計的世界,與面向對象有根本上的不同,它存在隐式接口和編譯期多态。如下代碼:
template<typename T>
void doProcessing(T& w){
if (w.size() > 10 && w != someNastyWidget) {
T temp(w);
temp.normalize();
temp.swap(w);
}
}
- 本例中,w必須支援size,normalize和swap成員函數、copy構造函數、不等比較。這一組表達式(對template而言必須有效編譯)便是T必須支援的一組隐式接口。
- 凡涉及w的任何函數調用,例如operate>和operate!=,有可能造成template具現化,使這些調用得以成功。這樣的具現行為發生在編譯期。“”以不同的template參數具現化function templates”會導緻調用不同的函數,這便是所謂的編譯期多态。
顯示接口由函數的簽名式(函數名稱、參數類型、傳回類型)構成。隐式接口就完全不同了。他是由有效的表達式組成:
請記住
- classes和templates都支援接口和多态。
- 對classes而言接口是顯示的,一函數簽名為中心,多态則是通過virtual函數發生于運作期。
- 對template參數而言,接口是隐式的,奠基于有效表達式。多态則是通過template具現化和函數重載解析發生于編譯期。
條款42:了解typename的雙重意義
對于template聲明式中,class和typename有什麼不同?
template<class T> class Widget;
template<typename T> class Widget;
其實兩者并沒有什麼不同,當我們聲明template類型參數,class和typename的意義完全相同。
然而C++并不總是把class和typename視為等價。
template<typename C>
void print2nd(const C& container)
{
if(container.size() >= 2){
C::const_iterator iter(container.begin());
}
}
如果解析器在template中遭遇一個嵌套從屬名稱,它便假設這個名稱不是個類型,除非你告訴它是。是以預設情況下嵌套從屬名稱不是類型。是以我們需要告訴C++它是類型,此時隻需要在緊鄰它之前放置關鍵字typename即可:
然而在typename不能出現在base class list中以及成員初值清單中作為基類修飾符。
template<typename T>
class Derived: public Base<T>::Nested { //base class list不允許出現typename
public:
explicit Derived(int x)
:Base<T>::Nested(x) //成員初值清單,不允許typename
{
typename Base<T>::Nested temp; //嵌套從屬類型名稱,此處允許typename
}
};
typedef和typename一起使用用來指涉一個名稱為成員類型而非成員變量,并為其設定typedef名稱(簡化):
template<typename IterT>
void workWithIterator(IterT iter)
{
typedef typename std::iterator_traits<IterT>::value_type value_type;
value_type temp(*iter);
}
請記住
- 聲明template參數時,字首關鍵字class和typename可互換。
- 請使用關鍵字typename辨別嵌套從屬類型名稱:但不得在base class lists及成員初值列内以它作為修飾符。