typename和class的區別
template<class C> void f(C& rc)
{
Typename C::iterator i = rc.begin();
// ...
}
編譯器不知道C的定義,所以不知道C::iterator是什麼東西。因此必須有typename來告訴編譯器。
template<typename T, template<typename T> class S>的S實際是模板引數,C++規定模板引數只能是class模板,所以這裡的class換成typename是不行的。
For instance:
The implementation of class A
class A
{
int a;
}
Another implementation of class A
class A
{
typedef int a;
}
Here, "typename A::a i" means the A::a is a type, not a data member. And "i" is an instance of type A::a
在模板定義時的class和typename是沒有區別的
class可以用來定義類,也可用作模板引數型別,而typename只能用作引數型別
template<class T> class Widget; // uses "class"
template<typename T> class Widget; // uses "typename"
在宣告一個 template type parameter(模板型別引數)的時候,class 和 typename 意味著完全相同的東西。
然而,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 parameterC 的型別。一個 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 variablevalue 具有 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
1. template parameters(模板引數)時,class 和 typename 是可互換的。
2. typename 去標識 nested dependent type names,在 base class lists中或在一個 member initialization list中作為一個 base class identifier時除外。
相關推薦
C++中typename和class的區別
type .get true 能夠 class .... ray pla 依賴 在c++Template中很多地方都用到了typename與class這兩個關鍵字,而且好像可以替換,是不是這兩個關鍵字完全一樣呢? 相信學習C++的人對class這個關鍵字都非常明白,clas
typename和class的區別
typename用來說明一個qualified name是一個型別。比如: template<class C> void f(C& rc) { Typename C::iterator i = rc.begin(); // ... } 編譯器不知道C
Typename和Class在宣告模板時的區別
宣告template引數時,字首關鍵詞class和typename可互換。也就是說以下兩個沒有區別: (1)template<class T>class Widget; (2)template<typename>class Widget。 然而C++
C++中typename和class在宣告模板時的區別
問題 在下面的 template declarations(模板宣告)中 class 和 typename 有什麼不同? template<class T> class Widget; // uses "class" template
定義模板時typename和class的區別
在c++Template中很多地方都用到了typename與class這兩個關鍵字,而且好像可以替換,是不是這兩個關鍵字完全一樣呢? 相信學習C++的人對class這個關鍵字都非常明白,class用於定義類,在模板引入c++後,最初定義模板的方法為: temp
c++模板中的 typename 和 class
在c++Template中很多地方都用到了typename與class這兩個關鍵字,而且在泛型程式設計的時候可以替換,但是 typename 和 class 並不是完全一致的。 相信學習C++的人對class這個關鍵字都非常明白,class用於定義類,在模板引入c++後,最初定義模板的方法
id和class的區別
xhtml 特殊符號 好的 web 沒有 定義 開頭 數字 推薦 id和class沒有本質上的區別,很多時候可以混用(XHTML不允許,但是也不會報錯)。但符合web標準的頁面必須結構良好,有語義,有可讀性,所以為了養成良好的代碼編寫習慣,建議讀者嚴格區分。同樣的i
值類型和引用類型的區別,struct和class的區別
tro 處理 數據結構和算法 ron ever ring net string 分配 C#值類型和引用類型 1、簡單比較 值類型的變量直接存儲數據,而引用類型的變量持有的是數據的引用,數據存儲在數據堆中。 值類型(value type):byte,short,int
public clas和class的區別
字節碼 public 一個 static ring rgs nbsp string clas public clas和class的區別 * 一個java源文件中可以定義多個class public class K { public static void main(St
Java中常量定義在interface和class的區別(轉)
var tac 不能被繼承 ble -o err 模式 variable 個人愛好 最終結論:定義常量在interface和class中其實都行,關鍵是看你的設計和個人愛好。 Java中interface中定義變量默認都是"public static final"類型的,
java詳析class和public class區別
通過 javac 關系 區別 文件 命令編譯 程序 需要 java public class和class的區別 類的定義有兩種方式: public class 類名 class 類名 我可以將class前面的public去掉,如果采用public cla
ID和Class有什麼區別?
這裡是修真院前端小課堂,每篇分享文從 【背景介紹】【知識剖析】【常見問題】【解決方案】【編碼實戰】【擴充套件思考】【更多討論】【參考文獻】 八個方面深度解析前端知識/技能,本篇分享的是: 【ID和Class有什麼區別?】 一、背景介紹 HTML頁面中的元
一文理解class.getClassLoader().getResourceAsStream(file)和class.getResourceAsStream(file)區別
基礎理解 都是實現獲取在classpath路徑下的資原始檔的輸入流。 為什麼是classpath而不是src,因為當web專案執行時,IDE編譯器會把src下的一些資原始檔移至WEB-INF/classes,classPath目錄其實就是這個classes目錄。這個目錄下放的一般
手遊客戶端的效能篇(二)----Unity和C#版之字串拼接,Struct和Class的區別與應用
接著上篇文章: 2、字串拼接(簡單,直接結論) 使用“a” + “b”在幾次(10次以內吧)連線是不會產生gc的但是大量連線就會產生; 連線多的用StringBuilder,內部
java之class和public class區別
在編寫類的時候可以使用兩種方式定義類:public class定義類和 class定義類 public class定義類: public class,類名稱應該與檔名稱完全一致。 class定義類: &
type介面和class的區別(型別和類)
Class public final class Class < T > implements java . io . Serializable, GenericDecla
java中Class.getMethods()和Class.getDeclaredMethods()的區別
在java中,可以根據Class類的物件,知道某個類(介面)的一些屬性(成員 ,方法,註釋,註解)等。由於最近的工作中用到了這些,其中需要在程式碼中格局反射知道某些類的方法,檢視文件的時候,看到了getMethods()和getDeclaredMethods()的差異。雖然兩者都能實現目的,但個人覺得還是
【C++】struct和class的區別
最近在看一些關於C++的書,然後這個問題不懂就來百度了= =這個文章寫的很好所以來分享~ C++中的struct對C中的struct進行了擴充,它已經不再只是一個包含不同資料型別的資料結構了,它已經獲取了太多的功能。 struct能包含成員函式嗎? 能! struc
class.forname和classloader區別
Java中class是如何載入到JVM中的: 1.class載入到JVM中有三個步驟 裝載:(loading)找到class對應的位元組碼檔案。 連線:(linking)將對應的位元組碼檔案讀入到JVM中。 初始化:(initializi
class-aware detector和class-agnostic detector區別
For a class-aware detector, if you feed it an image, it will return a set of bounding boxes, each box associated with the class of the o