在C++模板中,可以使用class或者typename來聲明模板參數,那么這兩個關鍵字有什么區別呢?
模板參數聲明
對于模板參數聲明,這兩個參數沒有區別,含義是一樣的。
template class Simple;template class Simple;
上面兩行都是聲明一個模板類Simple.
表明類型
假如我們有這樣一段代碼:
templatevoid add(const T &acontainer, T &sum){ T::const_iterator iter = container.begin(); for (; iter != container.end(); ++iter) { sum += *iter; }}
iter的類型是T::const_iterator,這個類型依賴模板參數T。把依賴模板參數的名稱稱為依賴名稱。當這個依賴名稱又在一個類中時,稱為嵌套依賴名稱。相對的,稱為非嵌套依賴名稱。
嵌套依賴名稱會導致編譯器編譯的困難,例如下面的代碼:
templatevoid add(const T &container){ T::const_iterator *x; ...}
這看起來像聲明一個變量x,它的類型為T::const_iterator *。但是編譯器并不知道,也有可能類T中又一個static數據成員const_iterator,或者正好有一個全局變量x。這時上面的代碼就變成乘法操作。這是因為 C++編譯器在處理模板的時候,會將需要推導的數據類型暫時擱置,到運行時再確定。
當編譯器遇到一個模板中的嵌套依賴名稱時,編譯器將它作為變量對待。因此,需要顯示的告訴編譯器,這就需要使用關鍵字typename。
templatevoid add(const T &container, T &sum){ typename T::const_iterator iter = container.begin(); for (; iter != container.end(); ++iter) { sum += *iter; }}
因此,使用嵌套依賴的類型名稱時,都需要使用typename指定它是一種類型。
例外
嵌套依賴名稱在基類列表中,或者在成員初始化列表中時,不能使用typename。
templateclass Drived: public Base::Nested { // 基類列表,不要使用typenamepublic: explicit Derived(int x): Base::Nested(x) { // 成員初始化列表,不要使用typename typename Base::Nested temp; ... } ...};
另外一些注意點
1、嵌套從屬名稱(nested dependent names)
假如template內出現的名稱如果依賴于某個模板參數,則稱其為從屬名稱(dependent names),如果從屬名稱在class內呈嵌套狀則稱之為嵌套從屬名稱(nested dependent names)。
例如:
templaet <typename T>void myPrint(const T& t){ t::const_iterator iter(t.begin());}
假設模板參數列表中的參數表示一個容器類型,則我們知道t::const_iterator一個依賴模板參數并且在容器內部,所以t::const_iterator是一個嵌套從屬名稱。
在我們知道t是什么之前沒有辦法可以知道t::const_iterator是否是一個類型,因為有還可能是個靜態(static)成員變量,考慮下面的例子:
template <typename T>void myPrint(const T& t){ t::const_iterator * x;}
如果const_iterator是t的靜態成員變量,則上面的t::const_iterator * x;中的*表示乘法,如果是個類型則表示聲明一個指向t::const_iterator類型的指針。
從而給編譯器造成困惑(因為我們不知道t是什么)。
C++有個規定:當解析器在模板中遇到一個嵌套從屬名稱時便假定這個名稱不是類型,除非你用關鍵字typename指定它是:
template <typename T>void myPrint(const T& t){ typename t::const_iterator * x; //這樣便不會造成困惑了}
同理不僅在內部,在參數列表里也是:
template <typename T>void f(const T& t, typename T::const_iterator cit){ //T不是嵌套從屬名稱,而T::const_iterator是,所以要在T::const_iterator前面加上typename //....}
template <typename T>class Derived: public Base<T>::Nested { //基類列表中不允許使用typenamepublic: explicit Derived(int x): Base<T>::Nested(int x){ //初始化列表中不允許使用typename typename Base<T>::Nested temp; //嵌套從屬名稱(既不在基類列表中又不在初始化列表中)前面必須要加typename }}
新聞熱點
疑難解答