一、意義①
- 意義①:typename可以在template中聲明類型參數
- 在template中聲明類型參數時,typename和class是等價的,兩者都可以
- 例如:
//兩者是等價的
template<class T> class Widget;
template<typename T> class Widget;
二、意義②
示範說明
- 現在我們有一個模闆,其接受一個STL容器類型,然後列印容器中的第二個元素的值,但是這個模闆可能會産生錯誤。代碼如下:
template<typename C>
void print2nd(const C& container)
{
if (container.size() >= 2) {
C::const_iterator iter(container.begin()); //初始化疊代器,綁定到第一個元素上
++iter;
int value = *iter;
std::cout << value;
}
}
- 關于“嵌套從屬名稱”和“非從屬名稱”的概念:
- 從屬名稱:
- 對于上面的iter來說,其是容器container的疊代器類型const_iterator,其依賴于template參數C的類型,是以我們稱const_iterator為從屬名稱(意義為:模闆内的一個名稱依賴于template的某個參數,那麼其就是從屬名稱)
- 當從屬名稱屬于class内時,我們稱其為嵌套從屬名稱。是以上面的const_iterator就是一種嵌套從屬名稱
- 非從屬名稱:上面的value變量,其類型就是int,不依賴于其他任何東西,是以我們稱int為非從屬名稱
- 上面的代碼之是以可能會産生錯誤,與從屬名稱有關系。我們現在考慮這樣的一個情況:
- 我們修改print2nd模闆,假設在其中定義一個疊代器指針,代碼如下
- 但是編譯器可能會這樣了解:const_iterator不是一個嵌套從屬名稱,而是一個類的static成員變量,x為一個局部變量。是以下面的代碼可能會被編譯器認為是兩個變量在相乘
template<typename C>
void print2nd(const C& container)
{
//...
//const_iterator可能被編譯器了解為C的static成員變量,x為一個變量,下面是兩個變量的相乘
C::const_iterator* x;
//...
}
- 正确的做法是為嵌套從屬名稱加上一個關鍵字typename,這樣可以顯式地告訴編譯器某種東西是一個類型,而不是其他東西。代碼如下:
template<typename C>
void print2nd(const C& container)
{
if (container.size() >= 2) {
//使用typename,顯式告訴編譯器,const_iterator是一個類型
typename C::const_iterator iter(container.begin());
++iter;
int value = *iter;
std::cout << value;
}
}
- 有了上面的規則之後,我們可以編寫下面的模闆函數,其接受一個容器類,和該容器的疊代器為參數。代碼如下:
template<typename C>
void print2nd(const C& container,typename C::const_iterator iter)
{
//...
}
typename的一個例外
- 當typename用來聲明類型時,其不可以出現在兩個地方:
- 我們假設Nested是基類中的一種類型。例如:
template<typename T>
class Derived :public Base<T>::Nested //此處不可以使用typename
{
public:
explicit Derived(int)
:Base<T>::Nested(x)//此處不可以使用typename
{
typename Base<T>::Nested temp; //此處可以使用typename
}
};
typename在traits機制中的運用
- 假設我們現在有這樣一個函數模闆,其接受一個疊代器類型,我們打算在函數中為疊代器所指的對象做一份副本。代碼如下:
template<typename IterT>
void workWithIterator(IterT iter)
{
typename std::iterator_traits<IterT>::value_type temp(*iter);
//...
}
- 此處我們使用到了iterator_traits<>模闆類,其實一種traits類(參閱條款47)。我們傳遞給其一個疊代器類型為其進行執行個體化,那麼我們就可以通過其value_type萃取出疊代器所指的容器的類型。例如:
- 如果IterT是list<string>::iterator,那麼value_type就代表string,temp的類型就是string
- 如果IterT是vector<int>::iterator,那麼value_type就代表int,temp的類型就是int
- 因為value_type也是一種内嵌類型,是以我們需要使用typename聲明其是一種類型
- 如果上面的代碼比較複雜,那麼我們還可以搭配typedef來使用。例如:
template<typename IterT>
void workWithIterator(IterT iter)
{
typedef typename std::iterator_traits<IterT>::value_type value_type; //為類型聲明别名
value_type temp(*iter); //使用類型定義變量
//...
}
- 關于typename的移植性問題:某些編譯器接受typename,而可能有少數編譯器不接受typename。是以其存在有移植性
三、總結
- 聲明template參數時,字首關鍵字class和typename可互換
- 請使用關鍵字typename辨別嵌套從屬類型名稱;但不得在base class lists(基類列)或member initialization list(成員初值列)内以它作為base class修飾符