typename的一些用法和注意問題


 

 

一些關鍵概念
在我們揭開真實原因的面紗之前,先保持一點神秘感,因為為了更好的理解C++標准,有幾個重要的概念需要先行介紹一下。

限定名和非限定名
限定名(qualified name),故名思義,是限定了命名空間的名稱。看下面這段代碼,cout和endl就是限定名:

#include <iostream>
int main()  {
    std::cout << "Hello world!" << std::endl;

}

cout和endl前面都有std::,它限定了std這個命名空間,因此稱其為限定名。
如果在上面這段代碼中,前面用using std::cout;或者using namespace std;,然后使用時只用cout和endl,它們的前面不再有空間限定std::,所以此時的cout和endl就叫做非限定名(unqualified name)。

 

依賴名和非依賴名
依賴名(dependent name)是指依賴於模板參數的名稱,而非依賴名(non-dependent name)則相反,指不依賴於模板參數的名稱。看下面這段代碼:

template <class T>
struct MyClass {
    int i;
    vector<int> vi;
    vector<int>::iterator vitr;
    T t;
    vector<T> vt;
    vector<T>::iterator viter;

};

因為是內置類型,所以類中前三個定義的類型在聲明這個模板類時就已知。然而對於接下來的三行定義,只有在模板實例化時才能知道它們的類型,因為它們都依賴於模板參數T。因此,T, vector<T>和vector<T>::iterator稱為依賴名。前三個定義叫做非依賴名。
更為復雜一點,如果用了typedef T U; U u;,雖然T沒再出現,但是U仍然是依賴名。由此可見,不管是直接還是間接,只要依賴於模板參數,該名稱就是依賴名。

typename的標記作用:
結束以上兩個個概念的討論,讓我們接着揭開typename的神秘面紗。
一個例子
在Stroustrup起草了最初的模板規范之后,人們更加無憂無慮的使用了class很長一段時間。可是,隨着標准化C++工作的到來,人們發現了模板這樣一種定義:

template <typename T>
void foo() {
    T::iterator iter;
    // ...
}

在上例代碼中T本身已經是模板的類型參數,它只有等到模板實例化時才會知道是哪種類型,更不用說由T定義的內部的iterator

編譯器不知道第三行代碼到底是,定義一個變量還是定義一個新類型,這樣同一行代碼能以兩種完全不同的方式解釋,而且在模板實例化之前,完全沒有辦法來區分它們,這絕對是滋生各種bug的溫床。這時C++標准委員會再也忍不住了,與其到實例化時才能知道到底選擇哪種方式來解釋以上代碼,委員會決定引入一個新的關鍵字,這就是typename

 

c++標准定義到:

對於用於模板定義的依賴於模板參數的名稱,只有在實例化的參數中存在這個類型名,或者這個名稱前使用了typename關鍵字來修飾,編譯器才會將該名稱當成是類型。除了以上這兩種情況,絕不會被當成是類型。

因此,如果你想直接告訴編譯器T::iterator是類型而不是變量,只需用typename修飾:

template <typename T>
void foo() {
    typename T::iterator iter;
    // ...
}

通過加上typename關鍵字,這樣編譯器就可以確定T::iterator是一個類型,而不再需要等到實例化時期才能確定,因此消除了歧義。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM