元程式設計技法 1 if c

2021-04-16 12:43:54 字數 3100 閱讀 9279

一直想整理一下對於meta-programming的一些想法,就從這個最簡單的開始吧!

if_c是boost::mpl庫提供的乙個元程式設計演算法,它接受三個模板引數,第乙個是bool型別,後面兩個是型別名:

template<

bool c

, typename t1

, typename t2

>

struct if_c

;它的作用是,如果c為true,則type為t1;反之type為t2。如果要用偽**來表達這個演算法的意思,就是

if(c)

type = t1

else

type = t2

現在可以來看看這個演算法的用途了,下面的模板函式printlarger接受兩個型別模板引數,並且在控制台輸出較大的乙個型別的名字。

template

void printlarger()

例如,在win32平台下,printlarger

會輸出double,而printlarger

輸出int。不要小看這個演算法,在元程式設計的世界裡,它扮演的角色非同小可。作為乙個基礎構件,它提供了在編譯期間進行邏輯判斷並選擇編譯「路徑」的方法,對於編譯期計算來說,其意義不下於執行時計算中的if...else語句。

這個演算法是怎樣實現的呢?很簡單,它用到的是在c++元程式設計中最常用的**:模板偏特化。通過模板偏特化來影響編譯器行為的做法在stl裡面比比皆是,只不過stl並沒有把它作為乙個演算法而已。而boost::mpl庫則把它提煉出來,為更高階的抽象提供了乙個很好的基礎。下面就來看看if_c演算法的實現。

利用模板偏特化的演算法一般分為兩個階段:一般定義階段和特化定義階段。一般定義提供了演算法的一般「形狀」,而特化定義則提供特殊路徑下的行為。if_c的一般定義是這樣的:

template<

bool c

, typename t1

, typename t2

>

struct if_c

;也就是說,直接令type=t1就可以。但是我們希望c==false的時候,type=t2,該如何處理呢?這就是特化定義上場的時候了。它特別指出,當c==false的時候,type應該為t2:

template<

typename t1

, typename t2

>

struct if_c

;

注意這裡的語法,特化值false不出現於模板引數列表中,而是直接跟在if_c之後。這兩個定義合起來告訴編譯器:if_c的第乙個模板引數是乙個bool值,如果它為false,那麼採取特化定義,也就是typedef t2 type這一句;在其他的情況下(在這裡,當然只有c==true啦),採取一般定義,也就是typedef t1 type。整個判斷在編譯期間就已經完成,不但不會消耗執行時期的時間,而且還可以輕易地辦到在執行時很不好實現的一些事情,如上述的根據大小選擇型別。

從if_c演算法中,我們可以一窺元程式設計的一些特點:首先,元程式設計操作的往往是型別,乙個演算法通過模板機制來接受引數,而通過內部的typedef來返回結果型別;其次,統一的命名對於元程式設計具有重要意義,在if_c中,結果型別被命名為type,而這個命名就像執行時演算法中的函式命名一樣,只要大家都知道並且遵守,就可以很好的交流。如果乙個使用者不知道if_c的計算結果型別叫做type,那就根本無法使用它。在元程式設計的世界裡,這樣一些約定叫做concept,concept可以看作是比型別更高的一層抽象,它規定了乙個型別應該像什麼樣子(有點類似於介面)。如果乙個型別滿足乙個concept的要求,那麼它就叫做這個concept的乙個model,從而就可以參與所有要求這個concept的計算。這一點有點類似於實現了乙個介面的類例項可以參與所有要求該介面的計算一樣。

了解了這個最簡單的演算法,下面可以對它進行一些推廣:我們希望讓第乙個模板引數不只是單純的bool型別,而是「任何可以被轉換成bool型別的」型別。可以把這個演算法命名為if_,這也是boost::mpl庫提供的演算法之一。我們希望這個演算法的形狀是:

template< 

typename t1

, typename t2

, typename t3

>

struct if_

;現在,希望if_的行為是:如果t1可以被轉換成true,則type=t2;如果t1可以被轉換成false,則type=t3;如果都不可以,那麼就應該報錯。幸運的是,編譯期演算法如果出錯,一定是編譯錯誤,不會造成執行時候的麻煩。

在這裡,需要解決的第乙個難題是:t1是乙個型別,而true和false都是乙個值,這兩者之間怎麼可能轉換呢?命名規範又要大顯身手了,我們可以約定:被用作t1的型別必須定義乙個「可被轉換成true或false的靜態成員」,名字叫value。有了這個約定,我們就可以放心大膽的編寫if_演算法,而使用者如果強行把不符合該約定的型別放到t1的位置,那麼編譯器一定會阻止這種愚蠢的做法。說到這裡,if_演算法的實現已經呼之欲出了,它直接利用了if_c演算法:

template<

typename t1

, typename t2

, typename t3

>

struct if_

;boost::mpl庫的if_演算法實現與這個異曲同工,只不過它的實現考慮了更多可移植性問題,支援lambda表示式,並且使用了boost::static_cast函式,**比較複雜。

現在只需要定義乙個帶有成員value的型別,就可以使用if_演算法了。

struct truet

;...

if_

::type v; //此處v為int型別的變數

在這個簡單的例子中,似乎if_比if_c麻煩了許多,因為if_c根本無須定義什麼truet,只需要直接傳入乙個true就可以達到目的。但是,使用型別作為引數讓if_可以直接使用其他的編譯期演算法,如果這些演算法都遵守關於定義乙個value成員的約定,那就可以節省不少的**。更重要的是,它提公升了抽象的級別:判斷的依據不但可以是bool值,而且是「任何可以得出bool值的型別」。當然,我們往往不需要更高的抽象,所以正如windows api很多都有增強的「ex版本」一樣,可以把if_看做if_c的增強版本,而我們應該根據場合選擇合適的版本。

元程式設計為我們開啟了乙個神奇的世界,在這裡可以實現許多在執行時世界裡不可思議的事情。最妙的是,這些實現都不會消耗執行時間,它們所占用的資源僅僅是空間和編譯時間而已。

元程式設計技法 1 if c

一直想整理一下對於meta programming的一些想法,就從這個最簡單的開始吧!if c是boost mpl庫提供的乙個元程式設計演算法,它接受三個模板引數,第乙個是bool型別,後面兩個是型別名 template bool c typename t1 typename t2 struct i...

元程式設計技法 1 if c

一直想整理一下對於meta programming的一些想法,就從這個最簡單的開始吧!if c是boost mpl庫提供的乙個元程式設計演算法,它接受三個模板引數,第乙個是bool型別,後面兩個是型別名 template bool c typename t1 typename t2 struct i...

模板元程式設計(1)

最近看到之前自己寫過的乙個小東西,可以作為模板元程式設計的開篇案例,所以就打算寫一系列的文章來講述一下c 中的模板元程式設計 模板元程式設計對於資深的c 程式設計師可能並不陌生,特別是用gcc做c 程式開發的,因為可以說gcc這個編譯器是實現c 11標準的最全的編譯器。不說題外話了,先介紹一下模板元...