文章分析如何在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 _2 : T2
_0 : T0 _1 : T1
根结点上,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>