template<typename C> // PRint 2nd element in void print2nd(const C& container) // container; { // this is not valid C++! if (container.size() >= 2) { C::const_iterator iter(container.begin()); // get iterator to 1st element ++iter; // move iter to 2nd element int value = *iter; // copy that element to an int std::cout << value; // print the int } } 我突出了這個函數中的兩個 local variables(局部變量),iter 和 value。iter 的類型是 C::const_iterator,一個依靠于 template parameter(模板參數)C 的類型。一個 template(模板)中的依靠于一個 template parameter(模板參數)的名字被稱為 dependent names(依靠名字)。當一個 dependent names(依靠名字)嵌套在一個 class(類)的內部時,我稱它為 nested dependent name(嵌套依靠名字)。C::const_iterator 是一個 nested dependent name(嵌套依靠名字)。實際上,它是一個 nested dependent type name(嵌套依靠類型名),也就是說,一個涉及到一個 type(類型)的 nested dependent name(嵌套依靠名字)。
print2nd 中的另一個 local variable(局部變量)value 具有 int 類型。int 是一個不依靠于任何 template parameter(模板參數)的名字。這樣的名字以 non-dependent names(非依靠名字)聞名。(我想不通為什么他們不稱它為 independent names(無依靠名字)。假如,像我一樣,你發現術語 "non-dependent" 是一個令人厭惡的東西,你就和我產生了共鳴,但是 "non-dependent" 就是這類名字的術語,所以,像我一樣,轉轉眼睛放棄你的自我主張。)
template<typename C> void print2nd(const C& container) { if (container.size() >= 2) { C::const_iterator iter(container.begin()); // this name is assumed to ... // not be a type 這為什么不是合法的 C++ 現在應該很清楚了。iter 的 declaration(聲明)僅僅在 C::const_iterator 是一個 type(類型)時才有意義,但是我們沒有告訴 C++ 它是,而 C++ 就假定它不是。要想轉變這個形勢,我們必須告訴 C++ C::const_iterator 是一個 type(類型)。我們將 typename 放在緊挨著它的前面來做到這一點:
template<typename C> // this is valid C++ void print2nd(const C& container) { if (container.size() >= 2) { typename C::const_iterator iter(container.begin()); ... } } 通用的規則很簡單:在你涉及到一個在 template(模板)中的 nested dependent type name(嵌套依靠類型名)的任何時候,你必須把單詞 typename 放在緊挨著它的前面。(重申一下,我待會兒要描述一個例外。)
typename 應該僅僅被用于標識 nested dependent type name(嵌套依靠類型名);其它名字不應該用它。例如,這是一個取得一個 container(容器)和這個 container(容器)中的一個 iterator(迭代器)的 function template(函數模板):
template<typename C> // typename allowed (as is "class") void f(const C& container, // typename not allowed typename C::iterator iter); // typename required C 不是一個 nested dependent type name(嵌套依靠類型名)(它不是嵌套在依靠于一個 template parameter(模板參數)的什么東西內部的),所以在聲明 container 時它不必被 typename 前置,但是 C::iterator 是一個 nested dependent type name(嵌套依靠類型名),所以它必需被 typename 前置。
"typename must precede nested dependent type names"(“typename 必須前置于嵌套依靠類型名”)規則的例外是 typename 不必前置于在一個 list of base classes(基類列表)中的或者在一個 member initialization list(成員初始化列表)中作為一個 base classes identifier(基類標識符)的 nested dependent type name(嵌套依靠類型名)。例如:
template<typename T> class Derived: public Base<T>::Nested { // base class list: typename not public: // allowed eXPlicit Derived(int x) : Base<T>::Nested(x) // base class identifier in mem { // init. list: typename not allowed
typename Base<T>::Nested temp; // use of nested dependent type ... // name not in a base class list or } // as a base class identifier in a ... // mem. init. list: typename required }; 這樣的矛盾很令人討厭,但是一旦你在經歷中獲得一點經驗,你幾乎不會在意它。
讓我們來看最后一個 typename 的例子,因為它在你看到的真實代碼中具有代表性。假設我們在寫一個取得一個 iterator(迭代器)的 function template(函數模板),而且我們要做一個 iterator(迭代器)指向的 object(對象)的局部拷貝 temp,我們可以這樣做:
template<typename IterT> void workWithIterator(IterT iter) { typename std::iterator_traits<IterT>::value_type temp(*iter); ... } 不要讓 std::iterator_traits<IterT>::value_type 嚇倒你。那僅僅是一個 standard traits class(標準特性類)的使用,用 C++ 的說法就是 "the type of thing pointed to by objects of type IterT"(“被類型為 IterT 的對象所指向的東西的類型”)。這個語句聲明了一個與 IterT objects 所指向的東西類型相同的 local variable(局部變量)(temp),而且用 iter 所指向的 object(對象)對 temp 進行了初始化。假如 IterT 是 vector<int>::iterator,temp 就是 int 類型。假如 IterT 是 list<string>::iterator,temp 就是 string 類型。因為 std::iterator_traits<IterT>::value_type 是一個 nested dependent type name(嵌套依靠類型名)(value_type 嵌套在 iterator_traits<IterT> 內部,而且 IterT 是一個 template parameter(模板參數)),我們必須讓它被 typename 前置。
假如你覺得讀 std::iterator_traits<IterT>::value_type 令人討厭,就想象那個與它相同的東西來代表它。假如你像大多數程序員,對多次輸入它感到懼怕,那么你就需要創建一個 typedef。對于像 value_type 這樣的 traits member names(特性成員名),一個通用的慣例是 typedef name 與 traits member name 相同,所以這樣的一個 local typedef 通常定義成這樣:
·用 typename 去標識 nested dependent type names(嵌套依靠類型名),在 base class lists(基類列表)中或在一個 member initialization list(成員初始化列表)中作為一個 base class identifier(基類標識符)時除外。