typename
typename
是一個C++程序設計語言中的關鍵字。當用於泛型編程時是另一術語"class
"的同義詞。在第一版 ISO 標準完成之前的原始 C++ 編譯器中,typename
關鍵字還不是 C++ 語言的一部分,當時Bjarne Stroustrup使用class
關鍵字作為模板參數。現在typename
是首選關鍵字,但較舊的原始碼可能仍會看到使用class
關鍵字(例如,參見 Bjarne Stroustrup 於 1994 年出版的《The Design and Evolution of C++》與 Bjarne Stroustrup 於 2013 年出版的《The C++ Programming Language: Four Edition》中的原始碼示例之間的差異)。這個關鍵字用於指出模板聲明(或定義)中的非獨立名稱(dependent names)是類型名,而非變量名。以下是對於泛型編程中typename
兩種迥然不同的用法的解釋。
class
關鍵字的同義詞
這是一項C++程式語言的泛型編程(或曰「模板編程」)的功能,typename
關鍵字用於引入一個模板參數,例如:
// 定义一个返回参数中较大者的通用函数
template <typename T>
const T& max(const T& x, const T& y)
{
if (y < x) {
return x;
}
return y;
}
這種情況下,typename
可用另一個等效的關鍵字class
代替,如下代碼片段所示:
// 定义一个返回参数中较大者的通用函数
template <class T>
const T& max(const T& x, const T& y)
{
if (y < x) {
return x;
}
return y;
}
以上兩段代碼沒有功能上的區別。
類型名指示符
考慮下面的錯誤代碼:
template <typename T>
void foo(const T& t)
{
// 声明一个指向某个类型为T::bar的对象的指针
T::bar * p;
}
struct StructWithBarAsType {
typedef int bar;
};
int main() {
StructWithBarAsType x;
foo(x);
}
這段代碼看起來能通過編譯,但是事實上這段代碼並不正確。因為編譯器並不知道T::bar
究竟是一個類型的名字還是一個某個變量的名字。究其根本,造成這種歧義的原因在於,編譯器不明白T::bar
到底是不是「模板參數的非獨立名字」,簡稱「非獨立名字」(dependent name)。注意,任何含有名為「bar
」的項的類T,都可以被當作模板參數傳入foo()
函數,包括typedef
類型、枚舉類型或者變量等。
A name used in a template declaration or definition and that is dependent on a template-parameter is assumed not to name a type unless the applicable name lookup finds a type name or the name is qualified by the keyword typename.
意即出現上述歧義時,編譯器將自動默認bar為一個變量名,而不是類型名。
所以上面示例代碼中的T::bar * p
會被解釋為T::bar
乘以p
(但p無處可尋),而不是聲明p為指向T::bar類型的對象的指針。
實際上,就算StructWithBarAsType
里定義了bar
是int類型也沒用,因為在編譯foo()
時,編譯器還沒看到StructWithBarAsType
。
如果還有另一個名為StructWithBarAsValue
的類,如下:
struct StructWithBarAsValue {
int bar;
};
那麼,編譯器就會把foo()里的
T::bar * p
翻譯成訪問StructWithBarAsValue::bar
這個成員數據,但是StructWithBarAsValue
里的bar
並非靜態成員數據,所以還是會報錯。
解決問題的最終辦法,就是顯式地告訴編譯器,T::bar
是一個類型名。這就必須用typename
關鍵字,例如:
template <typename T>
void foo(const T& t)
{
// 声明一个指向某个类型为T::bar的对象的指针
typename T::bar * p;
}
這樣,編譯器就確定了T::bar
是一個類型名,p也就自然地被解釋為指向T::bar
類型的對象的指針了。