C++11实现placeholder

文章分析如何在C++11中实现简单的placeholder。


首先看看什么是placeholder:
for_each(arr.begin(), arr.end(), cerr << _0 << endl);
其中arr的类型是vector<int>,第三个参数是一个表达式,表达式中有一个操作数是_0。第三个参数含义是对这个表达式进行求值,得到一个对象x,x拥有operator ()(int a)的成员函数。整个表达式意味着把arr的每一个元素b取出来,然后调用x(b)。而x(b)的效果是将b输出到一行当中。其中的_0就是placeholder,在表达式中作为一个占位符存在,等待外部给出_0的类型和值的时候,整个表达式再求值。

这是个很酷的语法糖,在C++11中可以用lambda表达式代替,不过弄明白怎么实现对于模板元编程的能力会有所提升。下面来分析一下如何去实现placeholder:

考虑_0, _1这些占位符,首先搞明白它们在C++中的语法成份。出现在表达式中,而且表达式不是处于某个模板的环境,可以肯定在编译时能知道表达式的所有操作数的类型。

_0可以是一个形如被定义为make_placeholder0()的宏,也可以是T0 _0;之类的一个对象。不过这没有关系,我们只关心_0这个表达式的类型。可以看出_0应该具有类类型,operator << 能作用于该类型的对象上。占位符和其它操作数进行运算,会产生新的类型,新的类型还可以参加运算,最后使得cerr << _0 << endl这个表达式具有某个类型。我们假定这个产生的新的类型是TExpr,TExpr在运算后仍然是TExpr类型。如果把语法树的结点用类型标注一下,对于表达式_0 + _1 - _2应该是如下结果


                       - : TExpr
               + : TExpr      _2 : T2

         _0 : T0  _1 : T1


TExpr将拥有operator (),能够求值。但是目前看来TExpr上的operator () 具有不确定性:
根结点上,operator将使用两个操作数,把减法操作应用在两个操作数上。
在左边的结点上,是将加法应用在两个操作数上。

所以TExpr中的operator ()是多态的。如果将语法树改为:
                       - : TExpr<MinusTag>
               + : TExpr<AddTag>      _2 : T2
         _0 : T0  _1 : T1
事情就变得容易一些,TExpr是参数化的,根据不同的Tag参数,在operator ()时有不同的行为。

可以预见我们应该构造一堆这样的类:
template<typename L, typename R, typename T>
class TExpr;


template<typename L, typename R>
class TExpr<L, R, AddTag>
{
	L left_operand;
	R right_operand;
};


template<typename L, typename R>
class TExpr<L, R, SubTag>
{
	L left_operand;
	R right_operand;
};
其中L,R两个类型可能是T0,T1等占位符的类型,或者是TExpr<,,,>,或者是其它类型。但是这样做有个缺点,把参数个数写死了。于是将上面的AddTag和TExpr组合到一起,形成
template<typename T1, typename T2>
struct TExprAdd
{
};
template<typename T1, typename T2>
struct TExprSub
{
};
进一步,将_0和_1的类型定义为:
template<int v>
struct TExprBasic
{
};
并提供操作:
template<typename T1, typename T2>
TExprAdd<T1, T2> 
operator + (T1 a, T2 b)
{
	typedef TExprAdd<T1, T2> TI;
	return TI(a, b);
}


template<typename T1, typename T2>
TExprSub<T1, T2> 
operator - (T1 a, T2 b)
{
	typedef TExprSub<T1, T2> TI;
	return TI(a, b);
}
于是语法树变为:


                        - : TExprSub<TExprAdd<TExprBasic<0>, TExprBasic<1>>, TExprBasic<2>>
                + : TExprAdd<TExprBasic<0>, TExprBasic<1>>          _2 : TExprBasic<2>
     _0 : TExprBasic<0>  _1 : TExprBasic<1>

至此,我们已经给出了一个可以用的placeholder的架构了。

我们要求TExprAdd,TExprSub拥有一些共性,满足某个concept,这个concept就是TExpr。这个concept是自己在编程中心中默默建立的,当然也可以把这个concept用显式的方式写出来:
<pre name="code" class="cpp">template<typename T>
class TExpr
{
	// require 1:T具有计算返回类型的元函数get_result_type
	template<typename TUPLE>
	struct get_result_type
	{
		typedef typename T::template get_result_type<TUPLE> Impl;
		typedef typename Impl::result_type result_type;
	};
	
	// require 2:T的对象应该具有operator () 成员函数模板,用于求值
	template<typename... Arg>
	auto operator () (Arg... arg)->typename get_result_type<std::tuple<Arg...>>::result_type
	{
		return impl.template operator () (std::forward<std::tuple<TL...>>(t));
	}
	
	T impl;
};

template<typename T1, typename T2>
TExpr<TExprAdd<T1, T2> >
operator + (T1 a, T2 b)
{
	typedef TExprAdd<T1, T2> TI;
	return TExpr<TI>(TI(a, b));
}

template<typename T1, typename T2>
TExpr<TExprSub<T1, T2> >
operator - (T1 a, T2 b)
{
	typedef TExprSub<T1, T2>
  • 3
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值