繼承模板類為什麼可以用this訪問基類?
effective C++ 中43條中提到三種訪問模板基類的方法,另兩個還行,為什麼用this也可以?這不是派生類嗎,this也應該是派生類的對象指針呀。初學者不懂,忘大神指教:如下
template&
class A
{
public: void f()
{ cout &<&< "A::f()" &<&< endl; } } template&
class B:public A&
{
public: void f()
{
this-&>f();//加this-&>為什麼可以訪問基類,這特意加了一個同名的,也同樣訪問基類的
A&::f();//也可以這個我懂
}
}
關鍵點在於:不加 this,編譯器就會把 f 當作一個非成員函數,因此才報的錯。你沒看錯,是「非」成員函數。
這事得從模板的「二段式名字查找」(Two-Phase Name Lookup)說起。
根據 C++ 標準,對模板代碼中的名字的查找,分為兩個階段進行:
- 模板定義階段:剛被定義時,只有模板中獨立的名字(可以理解為和模板參數無關的名字)參加查找
- 模板實例化階段:實例化模板代碼時,非獨立的名字才參加查找。
template&
class A
{
public:
void f() { std::cout &<&< "A::f()" &<&< std::endl; }
}
template&
class B: public A&
{
public:
void g() {
f();
}
}
如果沒有用模板,事情會簡單很多。然而這裡的 B 本身是模板,需要進行二段式名字查找。
首先進入 B 的模板定義階段,此時 B 的基類 A&
當稍晚些時候進入 B 的模板實例化階段時,編譯器已經堅持認為 f 是非成員函數,縱使此時已經可以查到 A&
「查非成員函數為什麼要去基類裡面查呢?」於是就找不到了。
那我們回過頭來看 this-&>f():- 模板定義階段:儘管沒法查到 A&
::f(),但明晃晃的 this-&> 告訴編譯器,f 是一個成員函數,不是在 B 類里,就是在 B 類的基類里,於是編譯器記住了 - 模板實例化階段:此時編譯器查找的對象是一個「成員函數」,首先在 B 中查,沒有找到;然後在其基類里查,於是成功找到 A&
::f(),功德圓滿。
第一贊已經講的很好了。二段式命名查找,就是這個概念。lz你最開始提問的那段代碼應該是會導致遞歸調用棧溢出。如果是第一贊那位的代碼(f改成g),就是如同他解釋的那樣。另外,這個二段式查找的實現,也和不同編譯器版本的特性支持有關。似乎很老的編譯器如vc6.0之類就不支持。
2015-07-01 補充這篇文章很好的講解了二段查找及其所解決的問題:Standard Features Missing From VC++ 7.1. Part III: Two-Phase Name Lookup。同時,文章和評論也討論了升級到支持二段查找的編譯器版本可能break現有代碼行為,有些會產生error(比較好解決),有些則沒有警告(難以發現),比如:
void foo( int a ) { /* Some definition */ }
// Now write a template that uses foo in a non-dependent way
// but passing in a char parameter.
void foo( char a ) { /* Some definition */ }
// Now use the template.
所以如果有模板代碼,必須檢查是否有unqualified but dependent names並加以更正。。。以保證代碼的跨編譯器一致性。
-------------------------------------------------------------------------------------------------------------------------------
任何一個抽象的模板類,在你用到它的時候(instantiate),他已經變成了一個具體類(模板參數已經被替換)。所以你的問題等價於:下面這個代碼為什麼成立?你體會一下:
class A
{
public:
void f() { cout &<&< "A::f()" &<&< endl; }
}
class B : public A
{
public:
void g() { this-&>f(); }
}
template&
class A
{
public:
void f() { std::cout &<&< "A::f()" &<&< std::endl; }
};
template&
class B : public A&
{
public:
B() { std::cout &<&< "Test()
"; }
void g() {
--fxx();
}
};
int main()
{
B&
//b.g(); // 用到這句編譯器才會給出error
return 0;
}
this-&>f()
這個訪問的是 B::f() ,不是 A&
按照題目中的現象:
template&
class A
{
public: void f()
{ cout &<&< "A::f()" &<&< endl; }
}
template&
class B:public A&
{
public: void f()
{
this-&>f();//加this-&>為什麼可以訪問基類,這特意加了一個同名的,也同樣訪問基類的
A&
}
}
這樣不管加不加this,都會出現堆棧溢出。 函數f中一直遞歸調用,沒有出口。
覺得LZ的原意應該是想問,template &
class A
{
public:
void f() {printf("A");}
};
template & 這個應該跟編譯器實現有關,至少VS2010 這樣編譯不會有問題。
class B : public A&
{
public:
void g()
{
f();
this-&>f(); //?
A&
}
};
應該是未定義行為吧。第一階段猜測錯誤的話(指編譯器認為f()處於全局空間內),第二階段可以彌補:重新查找依賴名字即可。PS:gcc編譯失敗,msvc編譯通過。
推薦閱讀:
※c++模板類拷貝構造函數的問題,有點疑惑?
※std::move(expr)和std::forward(expr)參數推導的疑問?
※C++ delete[] 是如何知道數組大小的?
※面向對象編程(oop)從誕生到現在理念上經歷了幾次怎樣大的變遷和轉化?
※C++ 11為什麼引入nullptr?