1. 程式人生 > >typename 你會用了嗎?

typename 你會用了嗎?

相信typename在模板中用做作class關鍵詞的同義詞大家都懂的,但是typename用作型別名指示符確實很少用,作用在於指示編譯器一些名字是型別名還是變數名。

詳細例子如下:

型別名指示符

考慮下面的錯誤程式碼:

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到底是不是“模板引數的非獨立名字”,簡稱“非獨立名字”。[2]注意,任何含有名為“bar”的項的類T,都可以被當作模板引數傳入foo()函式,包括typedef型別、列舉型別或者變數等。

為了消除歧義,C++語言標準規定:

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 會被解釋為乘法,而不是宣告p為指向T::bar型別的物件的指標

如果還有另一個名為StructWithBarAsValue型別,如下:

struct StructWithBarAsValue {
    int bar;
};

那麼,編譯器將以完全不同的方式來解釋

T::bar * p

的含義。

解決問題的最終辦法,就是顯式地告訴編譯器,T::bar是一個型別名。這就必須用typename關鍵字,例如:

template <typename T>
void foo(const T& t) { // 宣告一個指向某個型別為T::bar的物件的指標 typename T::bar * p; }

這樣,編譯器就確定了T::bar是一個型別名,p也就自然地被解釋為指向T::bar型別的物件的指標了。