C++ 模板編譯採用兩階段名稱查找機制,理解它對編寫正確模板代碼至關重要。
模板定義時,編譯器解析不依賴模板參數的名稱。此時模板參數未知,基類模板實例也未實例化,故不查看基類作用域。
示例:
typedef double E;
template class X { typedef int E; };
template class Y : public X { E f() {} }; // f() 返回 double!
Y::f 返回類型 E 不依賴 T,階段一解析,只見全局 typedef double E,故返回 double。若為普通類繼承,則見基類 typedef int E 返回 int——這是模板與非模板的關鍵差異。
模板實例化時(如 Y),編譯器解析依賴模板參數的名稱。此時基類已實例化,作用域可見。
關鍵規則:若需調用基類模板成員,必須顯式指明依賴性。
this->member:this 類型依賴 T,故 this->member 為依賴名稱,延至階段二查找基類。Base::member :顯式限定基類模板實例。using Base::member; :引入基類成員到派生類作用域(C++11)。
void g() { cout << "global"; }
template class X { void g() { cout << "X::g"; } };
template class Y : public X {
void f() { g(); this->g(); } // 第一個調用 global,第二個調用 X::g
};
g() 非依賴 → 階段一見全局 g。
this->g() 依賴 → 階段二見 X。
若寫 g() 但無全局 g,且意圖調用基類 g,編譯器報錯:
error: there are no arguments to 'g' that depend on a template parameter, so a declaration of 'g' must be available
這正是提示你:「這不是依賴名稱,我現在就要找聲明,找不到就報錯。」
this->。typename,模板成員前加 template(如 this->template func())。using Base::member; 批量引入(C++11)。兩階段查找是 C++ 模板為了「儘早報錯」與「支持分離編譯」的妥協設計。核心口訣:不依賴參數 → 定義時查;依賴參數 → 實例化時查;想查基類 → 必須顯式依賴。深入理解可避免 90% 的模板名稱查找坑。