C++中typename技術探索
【原創文章,轉載請保留或註明出處:http://blog.csdn.net/yinyhy/article/details/8672813】
1.前言
typename是一個C++中的關鍵字。當用於泛型編程時是另一術語"class"的同義詞。這個關鍵字用於指出模板聲明(或定義)中的非獨立性名稱(dependent names)是類型名,而非變量名。
typename指示一個類型名,而非定義一個類型,以下聲明瞭一個Seq::iterator類型的變量itr,其中Seq是一個模板實例化時才知道的類:
typenameSeq::iterator itr;
如果沒有typename指示,Seq::iterator會被認爲是Seq的靜態變量,而不是類型名。typename關鍵字不會定義一個類型,如果你想定義一個新類型的話,你必須這樣:
typedef typenameSeq::iterator ITR;
2.typedef和typename
2.1typedef
類型說明的格式爲:
typedef 類型 定義名;
類型說明只定義了一個數據類型的新名字,而不是定義一種新的數據類型。定義名錶示這個類型的新名字。
例如: 用下面語句定義整型數的新名字:
typedef int SIGNED_INT;
使用說明後,SIGNED_INT就成爲int的同義詞了, 此時可以用SIGNED_INT定 義整型變量。例如: SIGNED_INT i, j;(與int i, j等效)。
但longSIGNED_INT i, j; 是非法的。
typedef同樣可用來說明結構、聯合以及枚舉和類。
說明一個結構的格式爲:
typedefstruct{
數據類型 成員名;
數據類型 成員名;
...
}結構名;
此時可直接用結構名定義結構變量了。
例如:
typedefstruct{
char name[8];
int class;
char subclass[6];
float math, phys, chem,engl, biol;
}student;
student Liuqi;
則Liuqi被定義爲結構數組和結構指針。
2.2typename
typename關鍵字告訴了編譯器把一個特殊的名字解釋成一個類型,在下列情況下必須對一個name使用typename關鍵字:
1)一個唯一的name(可以作爲類型理解),它嵌套在另一個類型中的。
2)依賴於一個模板參數,就是說:模板參數在某種程度上包含這個name。當模板參數使編譯器在指認一個類型時產生了誤解。
保險起見,你應該在所有編譯器可能錯把一個type當成一個變量的地方使用typename。就像上面那個例子中的T::id,因爲我們使用了typename,所以編譯器就知道了它是一個類型,可以用來聲明並創建實例。
3.typename示例
如果你的類型在模板參數中是有限制的,那你就必須使用typename
#include <iostream>
#include <typeinfo> // for typeid() operator
using namespace std;
template <typename TP>
struct COne { // default member is public
typedef TP one_value_type;
};
template <typename COne> // 用一個模板類作爲模板參數, 這是很常見的
struct CTwo {
// 請注意以下兩行
// typedef COne:one_value_type two_value_type; // *1
typedef typename COne:one_value_type two_value_type; //*2
};
// 以上兩個模板類只是定義了兩個內部的public類型, 但請注意第二個類CTwo的two_value_type類型
// 依賴COne的one_value_type, 而後者又取決於COne模板類實例化時傳入的參數類型.
int main()
{
typedef COne<int> OneInt_type;
typedef CTwo< OneInt_type > TwoInt_type;
TwoInt_type::two_value_type i;
int j;
if ( typeid(i) == typeid(j) ) // 如果i是int型變量
cout << "Right!"<< endl; // 打印Right
return;
}
以上例子在Linux下用G++ 2.93編譯通過, 結果打印"Right". 但是如果把*1行的註釋號去掉, 註釋,*2行, 則編譯時報錯, 編譯器不知道COne:one_value_type爲何物。通常在模板類參數中的類型到實例化之後纔會顯露真身, 但這個CTwo類偏偏又要依賴一個已經存在的COne模板類, 希望能夠預先保證CTwo::two_value_type與COne:one_value屬於同一類型, 這是就只好請typename出山, 告訴編譯器, 後面的COne:one_value_type是一個已經存在於某處的類型的名字(type name), 這樣編譯器就可以順利的工作了。
4.typename含義
typename:的含義有兩個:
1)typenamevar_name,表示var_name的定義還沒有給出,這個語句通常出現在模版的定義內。例如:
template
void f()
{
typedef typename T::A TA; // 聲明 TA 的類型爲 T::A
TAa5; // 聲明 a5 的類型爲 TA
typename T::Aa6; // 聲明 a6 的類型爲 T::A
TA *pta6; //聲明 pta6 的類型爲 TA 的指針
}
因爲T是一個模版實例化時才知道的類型,所以編譯器更對T::A不知所云,爲了通知編譯器T::A是一個合法的類型,使用typename語句可以避免編譯器報錯。
2)template <typename var_name > class class_name,表示var_name是一個類型,在模版實例化時可以替換任意類型,不僅包括內置類型(int等),也包括自定義類型class。這就是問題中的形式。換句話說,在template中,typename和class的意義完全一樣。
5.內嵌依賴類型名
這裏有三個詞,內嵌、依賴、類型名。那麼什麼是“內嵌依賴類型名(nesteddependent type name)”
請看SGI STL裏的一個例子,只是STL中count範型算法的實現:
template <class _InputIter, class_Tp>
typename iterator_traits<_InputIter>::difference_type
count(_InputIter __first, _InputIter __last, const _Tp& __value) {
__STL_REQUIRES(_InputIter, _InputIterator);
__STL_REQUIRES(typename iterator_traits<_InputIter>::value_type,
_EqualityComparable);
__STL_REQUIRES(_Tp, _EqualityComparable);
typename iterator_traits<_InputIter>::difference_type __n = 0;
for ( ; __first != __last; ++__first)
if (*__first == __value)
++__n;
return __n;
}
這裏有三個地方用到了typename:返回值、參數、變量定義。分別是
typename iterator_traits<_InputIter>::difference_type
typename iterator_traits<_InputIter>::value_type
typename iterator_traits<_InputIter>::difference_type __n = 0;
difference_type, value_type就是依賴於_InputIter(模板類型參數)的類型名。源碼如下:template <class_Iterator>
struct iterator_traits {
typedef typename _Iterator::iterator_category iterator_category;
typedef typename _Iterator::value_type value_type;
typedef typename _Iterator::difference_type difference_type;
typedef typename _Iterator::pointer pointer;
typedef typename _Iterator::reference reference;
};
內嵌是指定義在類名的定義中的。以上difference_type和value_type都是定義在iterator_traits中的。
依賴是指依賴於一個模板參數。typename iterator_traits<_InputIter>::difference_type中difference_type依賴於模板參數_InputIter。
類型名是指這裏最終要指出的是個類型名,而不是變量。例如iterator_traits<_InputIter>::difference_type完全有可能是類iterator_traits<_InputIter>類裏的一個static對象。而且當我們這樣寫的時候,C++默認就是解釋爲一個變量的。所以,爲了和變量區分,必須使用typename告訴編譯器。
6.參考文獻:
http://blog.csdn.net/wang37921/article/details/6014811
http://blog.sina.com.cn/s/blog_9151e7300100zzc5.html
http://3140618.blog.163.com/blog/static/74517972011222114728421/