C++症結字typename的深刻懂得。本站提示廣大學習愛好者:(C++症結字typename的深刻懂得)文章只能為提供參考,不一定能成為您想要的結果。以下是C++症結字typename的深刻懂得正文
成績:鄙人面的 template declarations(模板聲明)中 class 和 typename 有甚麼分歧?
template<class T> class Widget; // uses "class"
template<typename T> class Widget; // uses "typename"
謎底:沒甚麼分歧。在聲明一個 template type parameter(模板類型參數)的時刻,class 和 typename 意味著完整雷同的器械。一些法式員更愛好在一切的時光都用 class,由於它更輕易輸出。其別人(包含我自己)更愛好 typename,由於它暗示著這個參數不用如果一個 class type(類類型)。多數開辟者在任何類型都被許可的時刻應用 typename,而把 class 保存給僅接收 user-defined types(用戶界說類型)的場所。然則從 C++ 的不雅點看,class 和 typename 在聲明一個 template parameter(模板參數)時意味著完整雷同的器械。
但是,C++ 其實不老是把 class 和 typename 視為同等的器械。有時你必需應用 typename。為了懂得這一點,我們不能不評論辯論你會在一個 template(模板)中觸及到的兩種名字。
假定我們有一個函數的模板,它能獲得一個 STL-compatible container(STL 兼容容器)中持有的能賦值給 ints 的對象。進一步假定這個函數只是簡略地打印它的第二個元素的值。它是一個用懵懂的辦法完成的懵懂的函數,並且就像我上面寫的,它乃至不克不及編譯,然則請將這 些事前放在一邊——有一種辦法能發明我的愚昧:
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" 就是這類名字的術語,所以,像我一樣,轉轉眼睛廢棄你的自我主意。)
nested dependent name(嵌套依附名字)會招致解析艱苦。例如,假定我們加倍愚昧地以這類辦法開端 print2nd:
template<typename C>
void print2nd(const C& container)
{
C::const_iterator * x;
...
}
這看上去似乎是我們將 x 聲明為一個指向 C::const_iterator 的 local variable(部分變量)。然則它看上去如斯僅僅是由於我們曉得 C::const_iterator 是一個 type(類型)。然則假如 C::const_iterator 不是一個 type(類型)呢?假如 C 有一個 static data member(靜態數據成員)恰巧就叫做 const_iterator 呢?再假如 x 恰巧是一個 global variable(全局變量)的名字呢?在這類情形下,下面的代碼就不是聲明一個 local variable(部分變量),而是成為 C::const_iterator 乘以 x!固然,這聽起來有些愚昧,但它是能夠的,而編寫 C++ 解析器的人必需斟酌一切能夠的輸出,乃至是愚昧的。
直到 C 成為已知之前,沒有任何方法曉得 C::const_iterator 究竟是否是一個 type(類型),而當 template(模板)print2nd 被解析的時刻,C 還不是已知的。C++ 有一條規矩處理這個歧義:假如解析器在一個 template(模板)中碰到一個 nested dependent name(嵌套依附名字),它假定誰人名字不是一個 type(類型),除非你用其它方法告知它。缺省情形下,nested dependent name(嵌套依附名字)不是 types(類型)。(關於這條規矩有一個破例,我待會兒告知你。)
記住這個,再看看 print2nd 的開首:
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 平日界說成如許:
template<typename IterT>
void workWithIterator(IterT iter)
{
typedef typename std::iterator_traits<IterT>::value_type value_type;
value_type temp(*iter);
...
}
許多法式員最後發明 "typedef typename" 並列不太協調,但它是觸及 nested dependent type names(嵌套依附類型名)規矩的一個公道的附帶成果。你會相當快地習氣它。你究竟有著壯大的念頭。你輸出 typename std::iterator_traits<IterT>::value_type 須要若干時光?
作為停止語,我應當 說起編譯器與編譯器之間對環繞 typename 的規矩的履行情形的分歧。一些編譯器接收必須 typename 時它卻缺掉的代碼;一些編譯器接收不准 typename 時它卻存在的代碼;還有多數的(平日是老舊的)會謝絕 typename 湧現在它必須湧現的處所。這就意味著 typename 和 nested dependent type names(嵌套依附類型名)的交互感化會招致一些稍微的可移植性成績。
Things to Remember
·在聲明 template parameters(模板參數)時,class 和 typename 是可交換的。
·用 typename 去標識 nested dependent type names(嵌套依附類型名),在 base class lists(基類列表)中或在一個 member initialization list(成員初始化列表)中作為一個 base class identifier(基類標識符)時除外。