類範圍與命名空間續-----留有後續疑問

來源:互聯網
上載者:User

類範圍與命名空間續

(名字尋找規則)

 

在版本4中,有關於類範圍中的名字尋找有這麼一段話:

       迄今為止,在我們所編寫的程式中,名字尋找(尋找與給定的名字相匹配的聲明的過程)是相對直接的。

(1) 首先,在使用該名字的塊中尋找名字聲明,只需考慮在該項使用之前聲明的名字。

(2) 如果找不到該名字,則在包圍的範圍(外層範圍,也需要在使用之前)中尋找。

(3) C++中所有名字必須在使用之前聲明

(4) 在名字尋找期間,如果類範圍使用的名字不能確定為成員名,則在包含該類或者成員定義的範圍中尋找,以便找到該名字聲明。

       知道理論不見得理解了本質,或者說理解可能會有偏差。下面我們一如既往地做個小測試,測試程式如下:

//findName   標頭檔

class client{public:    client(){}   client(client& c){}   ~client(){}      void CallShow()   {        void show();        show();        //Show();  函數模板無法單獨聲明,而沒有前項聲                       //明是不可以調用的   } private:   int id;}; templateclass server{ public:    server(){}   server(server& s){}   ~server(){}      void CallShow()   {        void show();        show();        //Show();   }};
//findName.cpp#include<iostream>#include"findName"using namespace std; void show(){   cout<<"Wellcome to call me!"<<endl;} template<class T>void Show(){   cout<<"0 == "<<static_cast(0)<<endl;} int main(){   client cus;   cus.CallShow();      server hos;   hos.CallShow();      Show(); //注意無參模板函數調用   Show();   Show();}

 

#include"findName"

很容易明白,就是把findName檔案的內容直接拷貝放在當前位置,那麼在一個檔案中,我們很容易理解show,Show無法調用的緣故,我們不能在聲明之前使用名字。

   如果我們非得把程式調通,也不是沒有辦法,解決方案有兩種方案

方案一:

#include<iostream>void show(){   std::cout<<"Wellcome to callme!"<<std::endl;}template<class T>void Show(){   std::cout<<"0 =="<<static_cast(0)<<std::endl;}#include"findName"using namespace std; int main(){   client cus;   cus.CallShow();   server hos;   hos.CallShow();   Show();   Show();   Show();}

 

方案二:

#include<iostream>void show();template<class T>  //模板無前向聲明方式void Show(){   std::cout<<"0 =="<<static_cast(0)<<std::endl;}#include"findName"using namespace std;void show(){   std::cout<<"Wellcome to callme!"<<std::endl;}int main(){   client cus;   cus.CallShow();      server hos;   hos.CallShow();      Show();   Show();   Show();}

上述範圍風格其實是C++的全域範圍,我們可以使用C++的命名空間。

 

命名空間的名字尋找規則:

(1)  類成員函數優先(按照子類--->基類順序),一經找到停止。

(2)  若沒有,在相應的命名空間中找(如usingnamespace std;就在std中尋找)。

(3)  若還沒有,就根據參數所在命名空間進行尋找。

 

函數調用匹配原則:(自己理解的)

(1)首先匹配函數名,若能找到進行下一步匹配。

(2)接著匹配函數類型,若無法實現匹配(如參數是泛型),等執行個體化時再匹配。

(3)執行個體化時需要嚴格匹配。

也即一個模板可以在一個函數出現之前調用它,不過調用時的參數必須要有泛型參數,且執行個體化該模板時,被調函數必須在之前出現在相關命名空間。

 

我們先看一個簡單的例子:

#include<iostream>

#include<string>

template<class T>void CallShow(){ //Show(); //Show(std::string("empty"));} template<class T>void CallShow(T s){ //show(); Show(s); //Show(std::string("empty"));} template<class T>class display{public: void CallShow(T s) { Show(s); }}; void Show(){ std::cout<<"Empty Show!"<<std::endl;}void Show(std::string s){ std::cout<<"s = "<<s<<std::endl;}int main(){ CallShow(); std::string str = std::string("song"); CallShow(str); display d; d.CallShow(str);}

由此測試小程式,我們可以得出:

(1)  Show(s)調用成功的原因,是由於模板函數的執行個體化時才算是定義  

  處,而執行個體化必須在調用時發生。

(2)  Show() 調用出錯是因為,該處函數沒有泛型參數。所以編譯器可以直接作抉擇,那麼它會在在模板函數執行個體化之前匹配函數。此時Show()並沒有定義,所以調用失敗。

(3) Show(std::string("empty"))調用失敗同(2)。

(4)注意一下CallShow()的調用方式。

 

當把下面代碼置於namespace std中的話:

void Show(){   std::cout<<"Empty Show!"<<std::endl;} void Show(std::string s){   std::cout<<"s = "<<s<<std::endl;}
namespace std{void Show(){   std::cout<<"Empty Show!"<<std::endl;} void Show(std::string s){   std::cout<<"s = "<<s<<std::endl;}}

調用仍然成功,這個不太符合我們的理解,因為在調用Show時,我們並沒有在調用Show時註明名字域std::Show,這貌似不合理。其實這是名字尋找的第三條規則:根據參數的命名空間進行尋找。

在mian函數中寫兩條語句:

Show();

Show(std::string("test"));

不出所料,第一句編譯錯誤:找不到函數名。第二句執行成功。

當把std 改為stdl時,兩句話都編譯不通過。

 

當上述Show不置於namespace std中,而CallShow,display置於namespace std中時。

namespace std{template<class T>void CallShow(){   //Show();   //Show(std::string("empty"));} template<class T>void CallShow(T s){   //show();   Show(s);   //Show(std::string("empty"));} template<class T>class display{ public:   void CallShow(T s)   {        Show(s);   }};}

編譯不通過,錯誤很明顯使用CallShow,display時必須加上範圍限定std::。

如果都加上namespace std那麼執行也會完全通過。

 

如果代碼改為如下:

#include<iostream>

#include<string>

namespace std{void Show(int i){}template<class T>void CallShow(){ //Show(); //Show(std::string("empty"));} template<class T>void CallShow(T s){ //show(); Show(s); //Show(std::string("empty"));} template<class T>class display{ public: void CallShow(T s) { Show(s); }};} namespace std{void Show(){ std::cout<<"Empty Show!"<<std::endl;} void Show(std::string s){ std::cout<<"s = "<<s<<std::endl;}} int main(){ std::CallShow(); std::string str = std::string("song"); std::CallShow(str); std::display d; d.CallShow(str); //Show(); Show(std::string("test"));}

程式能實現正常功能。

但是如果把,下面Show的namespace std去掉的話,那麼就會出現編譯錯誤。此時CallShow裡只會匹配std中的那個Show,這是C++匹配規則。當在該命名空間中找到了就不會去外層範圍中尋找。且不同命名空間之間不會發生重載機制。

本例中,Std中的Show不會與全域的Show形成重載,且當在std中找到Show之後就不會向外層尋找(這其實是c語言中的名字屏蔽原則,局部屏蔽全域,可以用“::”顯示調用)。然後發現參數不匹配所以出錯。

但此處使用“::”不可以,原因不明。

如果去掉std中的Show定義就可以找到全域空間的Show正常運行。

 

一個小結論:命名空間可以直接使用全域空間中的名字,反之不可(必需用類似std::)。

 

我們再次改變代碼:

#include<iostream>

#include<string>

namespace std{void Show(int i){}template<class T>void CallShow(){ //Show(); //Show(std::string("empty"));}template<class T>void CallShow(T s){ //show(); Show(s); //Show(std::string("empty"));}template<class T>class display{public: void CallShow(T s) { Show(s); }};}int main(){ std::CallShow(); std::string str = std::string("song"); std::CallShow(str); std::display d; d.CallShow(str);}namespace std{void Show(){ std::cout<<"Empty Show!"<<std::endl;}void Show(std::string s){ std::cout<<"s = "<<s<<std::endl;}}

這段代碼能成功執行煞是費解,難道此處Show不是在執行個體化之後定義的嗎?這個問題留待以後學完模板吧。

 

 

 

 

 

 

 

 

 

 

 

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.