天天看點

Effective C++之條款41、42

條款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及成員初值列内以它作為修飾符。

繼續閱讀