準備實現meta programming的fold函數,發現本身缺乏佔位符實現,這樣傳入fold的transform op類(元函數)都不得不另外寫個外覆類,其實我以爲沒啥很差,簡單直接,說實話干擾什麼的沒那麼嚴重,一個功能塊裏能用fold的地方能有幾回?但動了佔位符這個念頭,就想嘗試實現一下。html
看一下實際情景:c++
template<typename TList, typename Init, class TransformOp>
struct fold_s {};
咱們可能會但願把push_back做爲運算子傳入fold_s中,從而實現循環迭代TList的每個元素,對其應用push_back。如:app
using type = fold_s<typelist<int, float, char>, nullist, push_back>::type;
問題是,push_back並非一個類,只是一個聲明,push_back<somelist, t>如此纔是一個真正的類,而通常只有類才能做爲實參傳入。ide
最直接的作法是寫個外覆類:函數
struct push_back_wrap
{
template<typename TList, typename T>
struct apply { using type = typename mpl::push_back<TList, T>::type; }; };
傳入fold_s而後調用apply:spa
template<typename TList, typename Init, class TransformOp>
struct fold_s
{
using sometype = typename TransformOp::apply<TList, T>::type; };
using type = fold_s<typelist<int, float, char>, nullist, push_back_wrap>::type;
咱們知道不少函數語言的一個特徵就是延遲計算。此處push_back_wrap中的嵌套類apply,使得push_back_wrap也具備延遲的特性,類型計算直到fold_s真正應用apply時設計
才發生。這就是meta programming中實現lambada的手法。缺點是咱們必需要在使用lambda元類的地方都默認假設apply存在。相比於它的強大功能,由於c++ mpl的c++11
限制致使這個小不便,咱們就忍忍吧。code
以上說明了一個佔位符將要應用的情境。下面就開始no zuo no die的處理吧。其實就是有些人不但願每次用flod_s時都要寫個外覆類,他們但願當flod_s須要傳入push_backorm
時就直接傳入push_back,好看好記些。很明顯那隻能傳入一個push_back的特化了。
fold< vector<int, float, char>, vector<>, push_back<_1, _2> >::type;
上邊的_1,_2就是佔位符了。push_back<_1, _2>就是咱們所討論的特化的。顯然_1, _2是個類,在上述語句中分別指vector<>,int,總之佔位符將指定你須要指定的位置。
這個特化既然取代了外覆類,那它必然提供了類似的功能。也就是push_back必然是個類型延遲的元函數類,它具備相似下面的結構:
struct push_back<...>
{
struct apply { type... }; };
那麼在fold_s內當調用push_back::apply時,顯然push_back必需要具有從參數列表中挑選指定參數的能力,天然的,這個任務就交給_1,_2佔位符了。實現的辦法你能夠
去查看boost mpl庫的作法,也可以使用我下邊的作法(須要c++11支持):
#ifndef HI_MPL_PLACEHOLDERS_H_INCLUDED
#define HI_MPL_PLACEHOLDERS_H_INCLUDED
//////////////////////////////////////////////////////////////////////
namespace hi { namespace mpl { //surport palceholders is too painful namespace placeholders { namespace helper { template<typename... TList> struct arglist { }; typedef arglist<> nullargs; template<unsigned int N, typename... TList> struct at; template<unsigned int N, typename T, typename... TList> struct at< N, arglist<T, TList...> > { typedef typename at< N - 1, arglist<TList...> >::type type; }; template<typename T, typename... TList> struct at< 0, arglist<T, TList...> > { typedef T type; }; } // end of placeholders::helper template<int n> struct Arg { template<typename ... TList> struct apply { using type = typename helper::at<n - 1, helper::arglist<TList...> >::type; }; private: }; using _1 = Arg<1>; using _2 = Arg<2>; using _3 = Arg<3>; using _4 = Arg<4>; } // end of placeholders } } #endif
如上,_1::apply<int, char, float>::type爲int, _2::apply<int, char, float>::type爲char。若不太清楚原理請參考:
http://www.cnblogs.com/flytrace/p/3551414.html
以上要注意的arglist是從0開始索引的,而外部Arg是從1開始索引的。
至此讓咱們把push_back<_1, _2>完成:
template<>
struct push_back< _1, _2 > { template<typename... TList> struct apply { using type = typename push_back< typename _1::apply<TList...>::type, typename _2::apply<TList...>::type>::type; }; };
fold_s把固定的一堆參數傳入時,push_back總能挑選到正確位置的參數。下面咱們來看看一個奇妙的改變,這將會讓你恍然大悟_1, _2佔位符的設計和來歷。
讓咱們把上面的代碼中全部_1,_2的地方所有調換位置,獲得一個新的特化:
template<>
struct push_back< _2, _1 >
{
template<typename... TList>
struct apply { using type = typename push_back< typename _2::apply<TList...>::type, typename _1::apply<TList...>::type>::type; }; };
使用這個新特化時,fold_s傳入的第二個參數將被放到push_back的第一個參數位置,而_2位於push_back第一個參數的樣子正好很形象的描述了這個行爲。
如今你明白了吧,push_back<_1,_2>和push_back<_2,_1>這2個特化組合在一塊兒,讓咱們有了可以指稱第一,第二個參數的能力。
這確實很是帥。很惋惜當參數個數n增加時,你須要覆蓋n!種特化。參數爲5時你將不得不寫120個特化。boost使用preprocessor來自動生成這些類,你仔細觀察上述類的結構,
確實都是能夠自動生成的。我表示看了preprocessor幾眼就要瞎掉,有興致再研究。下面是我寫的更簡單的自動構造宏:
#ifndef HI_MPL_SUPPORT_LAMBDA_H_INCLUDE #define HI_MPL_SUPPORT_LAMBDA_H_INCLUDE #define SUPPORT_LAMBDA_1_IMPL(classname, A1) \ template<> \ struct classname##< A1 > \ { \ template<typename... TList> \ struct apply \ { \ using type = typename classname##< \ typename A1::apply<TList...>::type>::type; \ }; \ }; #define SUPPORT_LAMBDA_2_IMPL(classname, A1, A2) \ template<> \ struct classname##< A1, A2 > \ { \ template<typename... TList> \ struct apply \ { \ using type = typename classname##< \ typename A1::apply<TList...>::type, \ typename A2::apply<TList...>::type>::type; \ }; \ }; #define SUPPORT_LAMBDA_3_IMPL(classname, A1, A2, A3) \ template<> \ struct classname##< A1, A2, A3 > \ { \ template<typename... TList> \ struct apply \ { \ using type = typename classname##< \ typename A1::apply<TList...>::type, \ typename A2::apply<TList...>::type, \ typename A3::apply<TList...>::type>::type; \ }; \ }; #define SUPPORT_LAMBDA_4_IMPL(classname, A1, A2, A3, A4) \ template<> \ struct classname##< A1, A2, A3, A4 > \ { \ template<typename... TList> \ struct apply \ { \ using type = typename classname##< \ typename A1::apply<TList...>::type, \ typename A2::apply<TList...>::type, \ typename A3::apply<TList...>::type \ typename A4::apply<TList...>::type>::type; \ }; \ }; #define SUPPORT_LAMBDA_5_IMPL(classname, A1, A2, A3, A4, A5) \ template<> \ struct classname##< A1, A2, A3, A4, A5 > \ { \ template<typename... TList> \ struct apply \ { \ using type = typename classname##< \ typename A1::apply<TList...>::type, \ typename A2::apply<TList...>::type, \ typename A3::apply<TList...>::type \ typename A4::apply<TList...>::type \ typename A5::apply<TList...>::type>::type; \ }; \ }; #define SUPPORT_LAMBDA_1(classname, P) \ SUPPORT_LAMBDA_1_IMPL(classname, P##1) #define SUPPORT_LAMBDA_2(classname, P) \ SUPPORT_LAMBDA_2_IMPL(classname, P##1, P##2) \ SUPPORT_LAMBDA_2_IMPL(classname, P##2, P##1) #define SUPPORT_LAMBDA_3(classname, P) \ SUPPORT_LAMBDA_3_IMPL(classname, P##1, P##2, P##3) \ SUPPORT_LAMBDA_3_IMPL(classname, P##1, P##3, P##2) \ SUPPORT_LAMBDA_3_IMPL(classname, P##2, P##1, P##3) \ SUPPORT_LAMBDA_3_IMPL(classname, P##2, P##3, P##1) \ SUPPORT_LAMBDA_3_IMPL(classname, P##3, P##1, P##2) \ SUPPORT_LAMBDA_3_IMPL(classname, P##3, P##2, P##1) #define SUPPORT_LAMBDA_4(classname, P) \ SUPPORT_LAMBDA_4_IMPL(classname, P##1, P##2, P##3, P##4) \ SUPPORT_LAMBDA_4_IMPL(classname, P##1, P##2, P##4, P##3) \ SUPPORT_LAMBDA_4_IMPL(classname, P##1, P##3, P##2, P##4) \ SUPPORT_LAMBDA_4_IMPL(classname, P##1, P##3, P##4, P##2) \ SUPPORT_LAMBDA_4_IMPL(classname, P##1, P##4, P##3, P##2) \ SUPPORT_LAMBDA_4_IMPL(classname, P##1, P##4, P##2, P##3) \ SUPPORT_LAMBDA_4_IMPL(classname, P##2, P##1, P##3, P##4) \ SUPPORT_LAMBDA_4_IMPL(classname, P##2, P##1, P##4, P##3) \ SUPPORT_LAMBDA_4_IMPL(classname, P##2, P##3, P##1, P##4) \ SUPPORT_LAMBDA_4_IMPL(classname, P##2, P##3, P##4, P##1) \ SUPPORT_LAMBDA_4_IMPL(classname, P##2, P##4, P##1, P##3) \ SUPPORT_LAMBDA_4_IMPL(classname, P##2, P##4, P##3, P##1) \ SUPPORT_LAMBDA_4_IMPL(classname, P##3, P##1, P##2, P##4) \ SUPPORT_LAMBDA_4_IMPL(classname, P##3, P##1, P##4, P##2) \ SUPPORT_LAMBDA_4_IMPL(classname, P##3, P##2, P##1, P##4) \ SUPPORT_LAMBDA_4_IMPL(classname, P##3, P##2, P##4, P##1) \ SUPPORT_LAMBDA_4_IMPL(classname, P##3, P##4, P##1, P##2) \ SUPPORT_LAMBDA_4_IMPL(classname, P##3, P##4, P##2, P##1) \ SUPPORT_LAMBDA_4_IMPL(classname, P##4, P##1, P##2, P##3) \ SUPPORT_LAMBDA_4_IMPL(classname, P##4, P##1, P##3, P##2) \ SUPPORT_LAMBDA_4_IMPL(classname, P##4, P##2, P##1, P##3) \ SUPPORT_LAMBDA_4_IMPL(classname, P##4, P##2, P##3, P##1) \ SUPPORT_LAMBDA_4_IMPL(classname, P##4, P##3, P##1, P##2) \ SUPPORT_LAMBDA_4_IMPL(classname, P##4, P##3, P##2, P##1) #define SUPPORT_LAMBDA(classname, n, prefix) \ SUPPORT_LAMBDA_##n(classname, prefix) #endif
在每一個你但願支持佔位符的類定義後邊,加上SUPPORT_LAMBDA這句宏,填入參數總數,佔位符前綴(可包含命名空間,默認佔位符必須以自己數字結束)。以下例子
template<typename T, typename... TList> struct push_back; template<typename T, typename... TList> struct push_back< typelist<TList...>, T> { typedef typelist<TList..., T> type; }; template<> struct push_back< nulllist > { typedef nulllist type; }; SUPPORT_LAMBDA(push_back, 2, placeholders::_);
以上這一套實現佔位符的辦法,比boost的要簡潔了不少。固然還缺乏匿名佔位符這樣的手法,這裏提供一個簡易的思路,望你有所得。