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
型別、列舉型別或者變數等。
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
型別的物件的指標了。