跟我学c++高级篇——模板元编程之二元函数应用说明

一、元函数的应用

在前面分析了元函数,基本通晓了元函数是什么。在实际的应用过程中,元函数大致可以为分以下几类:
1、返回类型即萃取型元函数,这个看前面提到的标准库的false_type等
2、返回类型值的元函数,如前面经常提到xxx_v
3、返回多个值的,这个就涉及到前面提到的对引用和常量的处理

template<>
struct MyType<int>
{
  using ref_type = int &;
  using const_ref_type = const int &;
  using v_type = int;
};

这种操作其实有点可能会造成歧义,同时过多的类型在一起处理,无形中增加代码的耦合性,只能说按需使用吧。
4、无参数元函数,也就是使用普通类来定义元函数

struct TypeInt
{
    typedef int type;
};

这种函数只有输出没有输入。
5、嵌套类元函数

template <typename T>
struct Data
{
  template <typename N>
  struct Value
  {
    typedef T t;
  };
};

6、高级(阶)元函数
高级元函数或者说高阶元函数,其实就是高阶元函数就是一个元函数的参数可以是元函数的元函数。说得简单一些就是元函数可以嵌套使用。和常见的一维二维三维图形一样,可以组合起来生成更高维。

元函数和宏的关系有点纠结,宏也是在编译期展开,经常可以实现一些复杂的宏,如MFC库中,就大量使用了这种方式。但宏是在编译时的预处理器进行解析的,这就导致一些优化的特性被限制无法使用。而模板一般来说是可以在整个编译期被处理的,而且宏的代码不如模板友好,如果使用不正确往往会产生大量的隐形冲突。所以现在一般在编程中,都不建议广泛的使用宏编程(常见的变量推荐使用const来定义,这和C语言还是有一定区别)。
但凡事有一弊也必有在利,宏在一些特定场景(固定的数据处理,编译条件处理等)还是有优势的,还是要实事求是的来根据需求来进行应用,不能非左即右,一棒子打死。

二、高阶元函数(higher-oreder function)

高阶元函数,其实就是元编程应用的一种。它可以通过元函数类和占位符表达式来传递或者返回元函数。下面看一个元函数类例子:

struct plus_f
{
  template <typename T1,typename T2>
  struct apply
  {
    typedef typename mpl::plus<T1,T2>::type type;
  };
};
template <class T, class D1, class D2>
quantity<
    T
  , typename mpl::transform<D1,D2,plus_f>::type  // new dimensions
>
operator*(quantity<T,D1> x, quantity<T,D2> y)
{
    typedef typename mpl::transform<D1,D2,plus_f>::type dim;
    return quantity<T,dim>( x.value() * y.value() );
}

元函数类一般是指内嵌一个对外开放的名为apply的元函数的类。其实就是用一个普通类将元函数包装一下,使其成为一个类型。因为元函数操作和返回的都是类型,所以元函数类也可以做为一个参数传递给另外一个元函数,同样也可以返回一个元函数类。上面的例子,如果在乘法的重载中直接输入mpl::plus就会报错,但是外覆一个类型就可以了。
这里需要澄清一个问题,很多从标准c++起步开始学习的,甚至进一步学习到模板的,在接触元编程时,对元函数的理解并不清晰,比如上面的元函数类,反复强调包装了一个元函数,可apply明明是一个结构体。但在元编程中,元函数不只操作值的,它还可以操作类型,也就是说,输入是类型,输出也是类型,而此时它也是符合元函数的定义的。在此时可以把模板的<>理解为函数的(),这就清楚了。
下面再看一个点位符实现的例子:

template <class D1, class D2>
struct divide_dimensions
  : mpl::transform<D1,D2,mpl::minus< \_1, \_2> > // forwarding again
{};

通过占位符实现高阶元函数transform,调用的参数中是一个元函数。

另外,这里提到元函数转发的概念,元函数转发一般是指通过元函数的组合来形成新的元函数,在《c++模板元编程》中使用一个继承的方式来实现了元函数的转发。看一下例子:

struct minus_f
{
  template <typename T1,typename T2>
  struct apply:boost::mpl::minus
  {};
};

这样做的好处是代码整洁了不少,其实就是通过继承的方式来通过基类的初始类引入需要的typedef typename那一长串的代码来实现的效果。其实,也可以使用其它的方式来实现相类似的功能。

三、例程

看一个比较完整的例子:

#include <boost/mpl/int.hpp>
#include <boost/mpl/vector.hpp>

namespace boost{namespace mpl {}}
namespace mpl = boost::mpl;
#include <boost/static_assert.hpp>
#include <boost/mpl/transform.hpp>
#include <boost/mpl/minus.hpp>
#include<boost/mpl/placeholders.hpp>
using namespace mpl::placeholders;

#include <boost/mpl/vector_c.hpp>
namespace {
typedef mpl::vector_c<int,1,0,0,0,0,0,0> mass;
typedef mpl::vector_c<int,0,1,0,0,0,0,0> length; // or position
typedef mpl::vector_c<int,0,0,1,0,0,0,0> time;
typedef mpl::vector_c<int,0,0,0,1,0,0,0> charge;
typedef mpl::vector_c<int,0,0,0,0,1,0,0> temperature;
typedef mpl::vector_c<int,0,0,0,0,0,1,0> intensity;
typedef mpl::vector_c<int,0,0,0,0,0,0,1> angle;
}

// base dimension:        m l  t ...
typedef mpl::vector_c<int,0,1,-1,0,0,0,0> velocity;     // l/t
typedef mpl::vector_c<int,0,1,-2,0,0,0,0> acceleration; // l/(t2)
typedef mpl::vector_c<int,1,1,-1,0,0,0,0> momentum;     // ml/t
typedef mpl::vector_c<int,1,1,-2,0,0,0,0> force;        // ml/(t2)
typedef mpl::vector_c<int,0,0,0,0,0,0,0> scalar;

#include <boost/mpl/equal.hpp>

template <class T, class Dimensions>
struct quantity
{
   explicit quantity(T x)
      : m_value(x)
   {}

   T value() const { return m_value; }

template <class OtherDimensions>
quantity(quantity<T,OtherDimensions> const& rhs)
: m_value(rhs.value())
{
BOOST_STATIC_ASSERT((
   mpl::equal<Dimensions,OtherDimensions>::type::value
));
}

private:
   T m_value;
};

quantity<float,length> l( 1.0f );
quantity<float,mass> m( 2.0f );


template <class T, class D>
quantity<T,D>
operator+(quantity<T,D> x, quantity<T,D> y)
{
return quantity<T,D>(x.value() + y.value());
}

template <class T, class D>
quantity<T,D>
operator-(quantity<T,D> x, quantity<T,D> y)
{
return quantity<T,D>(x.value() - y.value());
}

void test1() {
quantity<float,length> len1( 1.0f );
quantity<float,length> len2( 2.0f );

len1 = len1 + len2;   // OK
}

template <class Sequence1, class Sequence2, class BinaryOperation>
struct transform;  // returns a Sequence

#include <boost/static_assert.hpp>
#include <boost/mpl/plus.hpp>
#include <boost/mpl/int.hpp>
namespace mpl = boost::mpl;

BOOST_STATIC_ASSERT((
mpl::plus<
    mpl::int_<2>
  , mpl::int_<3>
>::type::value == 5
));

struct plus_f
{
template <class T1, class T2>
struct apply
{
   typedef typename mpl::plus<T1,T2>::type type;
};
};

template <class T, class D1, class D2>
quantity<
T
, typename mpl::transform<D1,D2,plus_f>::type  // new dimensions
>
operator*(quantity<T,D1> x, quantity<T,D2> y)
{
typedef typename mpl::transform<D1,D2,plus_f>::type dim;
return quantity<T,dim>( x.value() * y.value() );
}

template <class D1,class D2>
typename mpl::transform<D1,D2, mpl::minus<_1,_2> >::type

fff(D1,D2);

template <class D1, class D2>
struct divide_dimensions
: mpl::transform<D1,D2,mpl::minus<_1,_2> > // forwarding again
{};

template <class T, class D1, class D2>
quantity<T, typename divide_dimensions<D1,D2>::type>
operator/(quantity<T,D1> x, quantity<T,D2> y)
{
return quantity<T, typename divide_dimensions<D1,D2>::type>(
  x.value() / y.value());
}

#include <cassert>
#include <cmath>
int main()
{
quantity<float,mass> m(5.0f);
quantity<float,acceleration> a(9.8f);
quantity<float,force> f = m * a;

quantity<float,mass> m2 = f/a;
float rounding_error = std::abs((m2 - m).value());

assert(rounding_error < .001);
}
  • 这是《c++模板元编程》中的一个例子

四、总结

把元函数的概念吃透,把基础整理的清楚,在头脑里有一个完整的认识。再加上多看一些大型开源库的代码,就会把元编程基本的套路搞清楚。为什么要看大型的开源库呢?一个是一般只有在基础库里元编程用的才广泛;另外一个是大型库的代码的水平相对代表了c++编程水平的一个层次。当然,也有一些小而专的元编程的代码,这就需要大家去网上或者论坛上去寻找了。
总之,学好理论,结合实践,就能够把元编程掌握。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值