• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            大龍的博客

            常用鏈接

            統(tǒng)計(jì)

            最新評論

            被誤解的C++——模板和宏 (轉(zhuǎn))

            模板和宏
            前些日子,論壇里大打口水仗的時(shí)候,有人提出這樣一個論斷:模板本質(zhì)上是宏。于是,諸位高手為此好好辯論了一番。我原本也想加入論戰(zhàn),但是覺得眾人的言論已經(jīng)覆蓋了我的想法,所以也就作罷了。
            盡管沒有參與討論,但“模板究竟和宏有什么關(guān)系”這個問題,始終在我的腦海中上下翻飛。每當(dāng)我能夠放松下來的時(shí)候,這個問題便悄悄地浮現(xiàn)。(通常都是哄兒子睡下,然后舒舒服服地沖個熱水澡的時(shí)候:))。
            我思索了半天,決定做些實(shí)際的代碼,以了解兩者的差異。現(xiàn)在,我把試驗(yàn)的結(jié)果提交給大家,讓眾人來評判。
            模板和宏是完全兩個東西,這一點(diǎn)毋庸置疑。模板的一些功能,宏沒有;宏的一些功能,模板沒有。不可能誰是誰的影子。我們這里主要想要弄清的是,模板的本質(zhì)究竟是不是宏。
            需要明確一下,所謂“本質(zhì)”的含義。這里我假定:一樣?xùn)|西是另一樣?xùn)|西的“本質(zhì)”,有么后者是前者的子集,要么后者是通過前者直接或間接地實(shí)現(xiàn)的,要么后者的基礎(chǔ)原理依賴于前者。如果哪位對此設(shè)定心存疑議,那么我們就得另行討論了。
            首先,我編寫了一個模板,然后試圖編寫一個宏來實(shí)現(xiàn)這個模板的功能:
            template<typename T>
            class cls_tmpl
            {
            public:
            string f1() {
            strings=v.f()+”1000”;
            return s;
            }
            void f2() {
            v.g();
            }
            private:
            Tv;
            };
            下面是宏的模擬:
            #definecls_mcr(T) \
            class \
            {\
            public:\
            void f1() {\
            v.f();\
            }\
            void f2() {\
            v.g();\
            }\
            private:\
            Tv;\
            }
            當(dāng)我使用模板時(shí),需要這么寫:
            cls_tmpl<Tp1>ct;
            使用宏的版本,這么寫:
            cls_mcr(Tp1)cm;
            兩者寫法一樣。但是下列代碼便出現(xiàn)問題:
            cls_tmpl<Tp1>ct1;
            cls_tmpl<Tp1>ct2;
            ct1=ct2;//Ok,ct1和ct2是同樣的類型
            cls_mcr(Tp1)cm1;
            cls_mcr(Tp1)cm2;
            cm1=cm2;//編譯錯誤,cm1和cm2的類型不同
            由于cls_mcr(Tp1)兩次展開時(shí),各自定義了一遍類,編譯器會認(rèn)為他們是兩個不同的類型。但模板無論實(shí)例化多少次,只要類型實(shí)參相同,就是同一個類型。
            這些便說明,模板和宏具備完全不同的語義,不可能用宏直接實(shí)現(xiàn)模板。如果要使宏避開這些問題,必須采用兩階段方式操作:
            typedef cls_mcr(Tp1)cls_mcr_Tp1_;
            cls_mcr_Tp1_cm1;
            cls_mcr_Tp1_cm2;
            cm1=cm2;//同一個類型,可以賦值
            這反倒給了我們一個提示,或許編譯器可以在一個“草稿本”上把宏展開,然后通過用展開后的類名將所有用到的cls_mcr(…)替換掉。這樣便實(shí)現(xiàn)了模板。
            但事情并沒有那么簡單。請考慮以下代碼:
            class Tp1
            {
            public:
            string f() {
            return“X”;
            }
            };

            cls_tmpl<Tp1>ct1;
            ct1.f1();

            cls_mcr(Tp1)cm1;//編譯錯誤:Tp1不包含成員函數(shù)g()
            cm1.f1();
            盡管模板和宏的代碼一樣,但是編譯器卻給出了不同的結(jié)果。回溯到cls_tmpl和cls_mcr的定義,兩者都有一個f2()成員函數(shù)訪問了Tp1的成員函數(shù)g()。但是,模板的代碼并沒有給出任何錯誤,而宏卻有編譯錯誤。要解釋清楚這個差異,先得了解一下C++模板的一個特殊的機(jī)制:模板中的代碼只有在用到時(shí)才會被實(shí)例化。也就是說,當(dāng)遇到cls_tmpl<Tp1>時(shí),編譯器并不會完全展開整個模板類。只有當(dāng)訪問了模板上的某個成員函數(shù)時(shí),才會將成員函數(shù)的代碼展開作語義檢查。所以,當(dāng)我僅僅調(diào)用f1()時(shí),不會引發(fā)編譯錯誤。只有在調(diào)用f2()時(shí),才會有編譯錯:
            ct1.f2();//編譯錯誤,Tp1不包含成員函數(shù)g()
            這種機(jī)制的目的主要是為了減少編譯時(shí)間。但后來卻成為了泛型編程和模板元編程中非常重要的一個機(jī)制。(最早用于traits等方面,參見《C++ Template》一書。我在模擬屬性的嘗試中,也使用了這種機(jī)制,很好用。)
            相反,宏是直接將所有的代碼同時(shí)展開,之后在編譯過程中執(zhí)行全面的語言檢查,無論其成員函數(shù)使用與否。而模板一開始僅作語法檢查,只有使用到的代碼才做語義檢查和實(shí)際編譯。
            從這一點(diǎn)看出,即使允許宏在“草稿本”中展開,它同模板在展開方式上也存在著巨大的差別。僅憑這一點(diǎn),便可以否定“模板的本質(zhì)是宏”這個論斷。但是,如果我們把眼光放寬一些,是否可以這么認(rèn)為:盡管模板和宏采用了完全不同的展開方式,那么如果模板中的每個成員都看作獨(dú)立的宏,那么是否可以認(rèn)為模板是通過一組宏,而不是一個宏,實(shí)現(xiàn)的呢?
            讓我們來看模板cls_tmpl<>的成員函數(shù)f1():
            string f1() {
            strings=v.f()+”1000”;
            return s;
            }
            如果我們把f1看作一個宏, f1在需要時(shí)以宏的方式展開,然后正式編譯。當(dāng)然,我們首先必須將模板轉(zhuǎn)換成一組宏。如果哪個編譯器真是這樣做的,那么可以勉強(qiáng)地認(rèn)為這個編譯器是通過宏實(shí)現(xiàn)模板的。(不過這種樣子的“宏”,還能算宏嗎?)
            但是,當(dāng)我們考慮另一個問題,事情就不再那么簡單了。請看以下代碼:
            x=y;
            a=b;
            假設(shè)x、y、a、b都是int類型。這兩行代碼編譯后可能會變成如下等效的匯編代碼(實(shí)際上是機(jī)器碼):
            mov eax, y
            mov x, eax
            mov eax, b
            mov a, eax
            我們可以看到,這兩行代碼分別轉(zhuǎn)化成兩條匯編指令,所不同的是參與的內(nèi)存變量。可以認(rèn)為編譯器把賦值的匯編碼(機(jī)器碼)做成一個“宏”:
            #define assign(v1, v2) \
            mov eax, v2\
            mov v1, eax
            在編譯時(shí)用內(nèi)存變量(的地址)替換“宏”的參數(shù)。那么這種情況下,我們是否應(yīng)該認(rèn)為編譯器(或者說編譯)的本質(zhì)是宏呢?
            由于C++標(biāo)準(zhǔn)沒有規(guī)定用什么方式展開模板,而我們也很難知道各種編譯器是如何實(shí)現(xiàn)模板的,也就無從得知模板是否通過宏物理實(shí)現(xiàn)。但是,我個人的看法是,宏和模板都是語法層面的機(jī)制。如果一定要用宏這種語法層面的機(jī)制,來解釋模板的(物理)本質(zhì),那也太牽強(qiáng)附會了。
            我覺得比較合理的解釋是:如果一定要把宏和模板扯上什么“親戚關(guān)系”,那么說宏是模板的遠(yuǎn)方大表哥比較合理。兩者在技術(shù)上有一定的同源性。都是以標(biāo)識符替換為基礎(chǔ)的。但是,其他在方面,我們很難說它們有多大的相似性或者關(guān)系。宏是語法層面的機(jī)制,而模板則深入到語義層面。無論是語法、語義,還是具體的實(shí)現(xiàn),都沒有什么一樣的地方。
            至于“模板的本質(zhì)是宏”這種說法的始作俑者,可能是Stroupstrup本人。最初他提出模板(當(dāng)時(shí)稱為類型參數(shù))可以通過宏實(shí)現(xiàn)。但是不久以后,便發(fā)現(xiàn)他心目中的模板和宏有著天壤之別。于是,他和其他C++的創(chuàng)建者一起建立和發(fā)展了模板的各種機(jī)制。
            故事本該就此結(jié)束,但是這個說法卻越傳越廣。我猜想原因有可能兩種。其一是為了使一些初學(xué)者理解模板的基本特征,用宏來近似地解釋以下模板,使人容易理解。我曾經(jīng)對一些不開竅的同僚說:“如果你實(shí)在搞不清模板,可以把它理解成象宏那樣的東西。但是記住,它跟宏沒關(guān)系!”很多人話只聽半句。他們記住了前半句,扔掉了更重要的后半句。所以,我現(xiàn)在再也不說這樣的話了。
            另一種原因可就險(xiǎn)惡多了。一些試圖打壓C++的人總是不遺余力地貶損C++的各種特性,(C++的問題我們得承認(rèn),但是總得實(shí)事求是吧),特別是那些最強(qiáng)大的功能。而模板則是首當(dāng)其沖的。如果把模板和宏,這種丑陋的、臭名昭著的“史前活化石”聯(lián)系在一起,對于打擊C++的名聲有莫大的幫助。(即便C++社群,也非常積極地排斥宏)。
            實(shí)際上,模板的本質(zhì)是不是宏,根本沒有什么實(shí)際意義。即便是這樣,也絲毫不會影響模板的價(jià)值。很多高級的編程機(jī)制都是建立在傳統(tǒng)的技術(shù)之上的,比如虛函數(shù)就是利用函數(shù)指針表和間接調(diào)用實(shí)現(xiàn)的。從沒有人拿這一點(diǎn)說事。
            但是,很多人卻對模板大做文章,想借此說明模板在本質(zhì)上是落后的東西。以此欺騙世人,特別是那些懵懂的初學(xué)者。我寫此文的目的,就是實(shí)在忍受不了這種指鹿為馬的言論,借此反擊一下。
            另一方面,通過模板和宏的特性的比較,可以使我們更深入地了解和理解兩種機(jī)制的特性、能力和限制。溫故而知新,總會有新的收獲。

             

            本文來自CSDN博客,轉(zhuǎn)載請標(biāo)明出處:http://blog.csdn.net/zdl1016/archive/2007/06/21/1660480.aspx

            posted on 2010-03-31 10:05 大龍 閱讀(453) 評論(0)  編輯 收藏 引用


            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            久久国产免费直播| av国内精品久久久久影院| 久久久久亚洲精品男人的天堂| 国产精品无码久久四虎| 中文字幕精品久久久久人妻| 亚洲精品乱码久久久久久蜜桃不卡 | 亚洲精品视频久久久| 97精品依人久久久大香线蕉97| 99热成人精品热久久669| 久久久国产精华液| 九九久久自然熟的香蕉图片| 久久这里只有精品视频99| 久久久久亚洲精品日久生情| 四虎国产精品免费久久5151| 久久伊人五月丁香狠狠色| 国内精品伊人久久久久网站| 久久99国内精品自在现线| 亚洲午夜精品久久久久久app| …久久精品99久久香蕉国产| 久久国产劲爆AV内射—百度| 久久亚洲中文字幕精品一区四 | 久久久久国产亚洲AV麻豆| 少妇内射兰兰久久| 噜噜噜色噜噜噜久久| 久久精品亚洲乱码伦伦中文| 国产午夜精品理论片久久影视| 2020久久精品亚洲热综合一本| 欧美粉嫩小泬久久久久久久 | 狠狠久久亚洲欧美专区| 77777亚洲午夜久久多喷| 狠狠色丁香婷婷久久综合| 日本久久久久久久久久| 久久精品无码一区二区日韩AV| 国产91久久综合| 国产精品免费久久久久久久久| 99久久www免费人成精品| 狠狠色婷婷综合天天久久丁香| 久久国产一区二区| 香蕉久久一区二区不卡无毒影院| 51久久夜色精品国产| 国产综合免费精品久久久|