准备实现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的要简洁了不少。固然还缺乏匿名占位符这样的手法,这里提供一个简易的思路,望你有所得。