C++模板元編程之使用模板,對數組進行“編譯期間求和”

來源:互聯網
上載者:User

編譯期間求值,將計算提前到編譯期間進行,可以最大限度地榨乾編譯器的潛力,提高程式的運行速度,用Andrei Alexanderescu的話說就是“時間花在編譯期,就某種意義來說這是‘免費的’”(《C++設計新思維》P55)。所以就有了對數組在編譯期間求和的需求。

先上代碼,再解釋:

#include <iostream>using namespace std;//一個全域的int數組,需要對其求和const int CONST_ARRAY[5]={1,2,3,4,5};//去掉const屬性後,編譯也可以通過//一個類模板,用於對數組求和template<int index>struct SumArrInCompilePhaseCls{static const int SUM;};//通過遞迴定義模板的方式求SUM的值template<int index>const int SumArrInCompilePhaseCls<index>::SUM=CONST_ARRAY[index]+SumArrInCompilePhaseCls<index-1>::SUM;//亮點在這裡//一個完全特化的類模板,用於結束模板的遞迴定義template<>struct SumArrInCompilePhaseCls<0>{static const int SUM;};const int SumArrInCompilePhaseCls<0>::SUM=CONST_ARRAY[0];int main(){        //使用模板,進行編譯期間的數組求和cout<<SumArrInCompilePhaseCls<4>::SUM<<endl;//結果15}

SUM確實是在編譯階段計算的,因為SUM是static const類型,該類型在編譯完成之後,就固定了,無法在運行期間改變!

仔細研究上面一段代碼,就會發現,在定義非特化類模板的SUM值時,使用了模板的遞迴定義:

//通過遞迴定義模板的方式求SUM的值template<int index>const int SumArrInCompilePhaseCls<index>::SUM=CONST_ARRAY[index]+SumArrInCompilePhaseCls<index-1>::SUM;

編譯器在編譯該運算式時,由於SumArrInCompilePhaseCls<index-1>類型未知,所以編譯器會產生SumArrInCompilePhaseCls<index-1>,繼而產生SumArrInCompilePhaseCls<index-2>,SumArrInCompilePhaseCls<index-3>,……還有好多~。也就是說,該運算式,導致編譯器遞迴產生多個SumArrInCompilePhaseCls模板類,而當index終於遞減到0時,即要產生SumArrInCompilePhaseCls<0>時,編譯器“驚喜”地發現,SumArrInCompilePhaseCls<0>已經實現了!所以編譯器就停止繼續產生"SumArrInCompilePhaseCls模板類"了,遞迴結束了!

由於所有“SumArrInCompilePhaseCls模板類”裡的SUM都是static const值,所以對於const值,就可以在編譯期間求和。

結束了嗎?

木有!

上面的代碼有兩個小問題:

1、調用時,輸入的index如果為負數(比如-1),編譯期間出現遞迴棧溢出,編譯失敗!因為負數無法“遞減到0",也就是說,遞迴不會停止!

2、輸入的index超過CONST_ARRAY的上限時(比如5),儘管編譯通過,但是運行結果錯誤,因為編譯期間求和的時候數組越界了!

好了,我們希望對數組求和的時候,可以判斷使用者的輸入是否合法!

可以通過增加一個“編譯期間的斷言”來解決這兩問題,這個斷言的源碼如下:

//一個編譯期間起斷言作用的類模板,用於保證數組下標始終非負template<bool isOk>struct CompilePhaseAssertion;template<>struct CompilePhaseAssertion<true>{};

使用的時候,可以這樣子:

//一個類模板,用於對數組求和template<int index>struct SumArrInCompilePhaseCls{static const int SUM;private:CompilePhaseAssertion<index>=0&&index<sizeof(CONST_ARRAY)/sizeof(int)> IsValidIndex;};

原理很明顯了吧?

CompilePhaseAssertion是個類模板,但是並沒有定義它!僅僅有一個參數為true的完全特化版本,所以,當模板參數isOk為false時,根本無法產生這種類型的執行個體,因為沒有isOk==false的模板類,因而導致編譯報錯。

通過在CompilePhaseAssertion中增加CompilePhaseAssertion執行個體的方式,保證index的範圍合法。

修改後的代碼如下:

#include <iostream>using namespace std;//一個全域的int數組,需要對其求和int CONST_ARRAY[5]={1,2,3,4,5};//一個編譯期間起斷言作用的類模板,用於保證數組下標始終非負template<bool isOk>struct CompilePhaseAssertion;template<>struct CompilePhaseAssertion<true>{};//一個類模板,用於對數組求和template<int index>struct SumArrInCompilePhaseCls{static const int SUM;private:CompilePhaseAssertion<index>=0&&index<sizeof(CONST_ARRAY)/sizeof(int)> IsValidIndex;};//通過遞迴定義模板的方式求SUM的值template<int index>const int SumArrInCompilePhaseCls<index>::SUM=CONST_ARRAY[index]+SumArrInCompilePhaseCls<index-1>::SUM;//一個完全特化的類模板,用於結束模板的遞迴定義template<>struct SumArrInCompilePhaseCls<0>{static const int SUM;};const int SumArrInCompilePhaseCls<0>::SUM=CONST_ARRAY[0];int main(){//使用模板,進行編譯期間的數組求和cout<<SumArrInCompilePhaseCls<4>::SUM<<endl;}

此時,如果使用以下兩種方式求和,則編譯報錯:

cout<<SumArrInCompilePhaseCls<-1>::SUM<<endl;//數組下標為負cout<<SumArrInCompilePhaseCls<5>::SUM<<endl;//數組上界越界

本文完。

參考資料:

1、《C++設計新思維》,Andrei Alexanderescu著,侯捷、於春景譯。

2、C++模板元編程技術與應用

3、榮威老師寫的其他文章或書籍

4、C++ 的MetaProgramming 入門篇

注意:

以上代碼在VS2005和G++中都能編譯通過,但是只有G++編譯出來的程式才能運行出正確結果(15),VS2005中的結果是錯誤的(5),原因還不清楚,如果您知道VS2005計算錯誤的原因,請及時留言通知我,我會非常感激您的!

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在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.