跟我学c++高级篇——模板元编程之三Policy

一、Policy

Policy,策略;要把它和设计模式中的策略(Strategy)区分开来,前者更倾向于编译期,而后者则是一种设计的框架模式,在上层应用设计中使用。 另外就是trait和policy这两个概念,前者是用来描述类型,而后者用来描述行为,有点类似于属性和方法。
Policy是一种策略,具体到实际就是一种具体的操作行为,这个比较容易理解,比如下棋采用双炮还是双车这就是一种策略。但是在实际使用中又会把Policy进行分组和抽象,那就是Policies和Policy Classes。

二、Policies和Policy Classes

Policies是面向语法的,而Policy是一种定义(常见的普通类接口是面向方法签名的)。这样理解就清楚了,这种定义方式如果是英文为母语或者十分了解英文的会发现理解上更清晰透彻,中文却不好翻译。Policy Classes,策略类别或者说策略组合,而实现了多个策略的Class又叫做hosts 或host classes。
一个policy可以实现多种policy classes,只要符合策略的接口即可。而在实际的应用中,可以用普通类做为Policy,也可以用模板为Policy,其实后者只是前者的更深一层的抽象。每个Policy对象都可以在实际编程中理解成为类和对象的关系,这样基本上就把这些基本的概念搞清楚了。
1、普通类做为Policy

class SumPolicy {
public:
template<typename T1, typename T2>
static void accumulate (T1& total, T2 const & value) {
total += value;
}
};

2、抽象为模板的Policy

template <typename T1, typename T2>
class SumPolicy {
public:
static void accumulate (T1& total, T2 const & value) {
total += value;
}
};

3、分析说明
这里要区分开Policy模板和Policy对象模板,前者指应用策略的模板,而后者表示实现策略的对象的模板,一个用,一个是生产。只要理解了基础的应用,再理解这些就好理解了,不用死记硬背。
通过上面的不断分析解剖,到现在应该基本明白什么是策略了,即在模板中控制其具体行为的参数就是Policy。

  • 更详尽的说明参看《c++Templates全览》 侯捷@译 和《Modern c++ Design》

三、例程

理解了策略,就可以在这个基础上进行“policy-based class”设计了。其实这种具体的设计就和设计模式中的策略类似了,通过策略来控制行为的选择。
下面看一个例子:

//普通类
#include <iostream>

template<typename T>
class AccumulationTraits;

template<>
class AccumulationTraits<char>
{
public:
    typedef int AccT;
    static AccT zero()
    {
        return 0;
    }
};

template<>
class AccumulationTraits<short>
{
public:
    typedef int AccT;
    static AccT zero()
    {
        return 0;
    }
};

template<>
class AccumulationTraits<int>
{
public:
    typedef long AccT;
    static AccT zero()
    {
        return 0;
    }
};

template<>
class AccumulationTraits<unsigned int>
{
public:
    typedef unsigned long AccT;
    static AccT zero()
    {
        return 0;
    }
};

template<>
class AccumulationTraits<float>
{
public:
    typedef double AccT;
    static AccT zero()
    {
        return 0;
    }
};
//...

class SumPolicy
{
public:
    template<typename T1, typename T2>
    static void accumulate(T1& total, T2 const& value)
    {
        total += value;
    }
};

template <typename T,
    typename Policy = SumPolicy,
    typename Traits = AccumulationTraits<T> >
    class Accum {
    public:
        typedef typename Traits::AccT AccT;
        //使用默认值来处理乘法结果为0为题,这是一种解决方案
        static AccT accum(T const* beg, T const* end,int init = 1)
        {
            AccT total = init?init:Traits::zero();
            while (beg != end)
            {
                Policy::accumulate(total, * beg);
                ++beg;
            }
            return total;
        }
};

class MultPolicy
{
public:
    template<typename T1, typename T2>
    static void accumulate(T1& total, T2 const& value)
    {
        total * = value;
    }
};

void TestAccum()
{
    int num[] = {1,2,3,4,5};
    std::cout << "all value is:" << Accum<int>::accum(&num[0], &num[5],0) << std::endl;
    std::cout << "all mult value is:" << Accum<int, MultPolicy>::accum(&num[0], &num[5]) << std::endl;
}
int  main()
{
    TestAccum();
    return 0;
}

此处主要是对Accum中计算时,增加了一个判断,否则初始化的zero选择零在乘法时可能会产生非预想的结果。
那么既然如此,是不是可以象上面提到的一样把其抽象化以后继续使用呢?

//模板类
#include <iostream>
template <typename T1, typename T2>
class SumPolicy {
public:
    static void accumulate(T1& total, T2 const& value) {
        total += value;
    }
};


template<typename T>
class AccumulationTraits;

template<>
class AccumulationTraits<char>
{
public:
    typedef int AccT;
    static AccT zero()
    {
        return 0;
    }
};

template<>
class AccumulationTraits<short>
{
public:
    typedef int AccT;
    static AccT zero()
    {
        return 0;
    }
};

template<>
class AccumulationTraits<int>
{
public:
    typedef long AccT;
    static AccT zero()
    {
        return 0;
    }
};

template<>
class AccumulationTraits<unsigned int>
{
public:
    typedef unsigned long AccT;
    static AccT zero()
    {
        return 0;
    }
};

template<>
class AccumulationTraits<float>
{
public:
    typedef double AccT;
    static AccT zero()
    {
        return 0;
    }
};

template<typename T1, typename T2>
class MultPolicy {
public:
    static void accumulate(T1& total, T2 const& value) {
        total * = value;
    }
};


template <typename T,
    template<typename, typename> class Policy = SumPolicy,
    typename Traits = AccumulationTraits<T> >
    class Accum {
    public:
        typedef typename Traits::AccT AccT;
        static AccT accum(T const* beg, T const* end) {
            AccT total = Traits::zero();
            while (beg != end) {
                Policy<AccT, T>::accumulate(total, * beg);
                ++beg;
            }
            return total;
        }
};
void TestTAccum()
{
    // create array of 5 integer values
    int num[] = { 1, 2, 3, 4, 5 };

    // print average value
    std::cout << "the average value of the integer values is "
        << Accum<int>::accum(&num[0], &num[5]) / 5
        << '\n';

    // print product of all values
    std::cout << "the product of the integer values is "
        << Accum<int, MultPolicy>::accum(&num[0], &num[5])
        << '\n';

    // create array of character values
    char name[] = "templates";
    int length = sizeof(name) - 1;

    // (try to) print average character value
    std::cout << "the average value of the characters in \""
        << name << "\" is "
        << Accum<char>::accum(&name[0], &name[length]) / length
        << '\n';
}
int  main()
{
    TestTAccum();
    return 0;
}

另外还可以通过偏特化的方式来支持自动的选择执行条件:

template<bool use_compound_op = true>
class SumPolicy {
public:
    template<typename T1, typename T2>
    static void accumulate(T1& total, T2 const& value) {
        total += value;
    }
};

template<>
class SumPolicy<false> {
public:
    template<typename T1, typename T2>
    static void accumulate(T1& total, T2 const& value) {
        total = total + value;
    }
};
template <typename T,
    typename Policy = SumPolicy<>,
    typename Traits = AccumulationTraits<T> >
    class Accum {
    public:
        typedef typename Traits::AccT AccT;
        static AccT accum(T const* beg, T const* end) {
            AccT total = Traits::zero();
            while (beg != end) {
                Policy::accumulate(total, * beg);
                ++beg;
            }
            return total;
        }
};

上面的代码比较老,条件分支在编译期控制也可以在c++17中使用constexpr,这个在前面分析,这里不重复。同时,此处涉及到了“模板的模板参数”,这个回头会专门进行分析说明,此处先空中掠过。另外,等以后会专门开辟一篇文章来写一个c++11以后的Policy的应用。

四、总结

Policy是一种个体的Action的抽象,或者说算法实现的抽象。对学编程的人来说,程序就是算法加数据结构。从某种意义上讲,它其实是算法实现组装的一个重要手段。把共性的算法行为形成Policy,再为设计所使用,其实就是设计的依赖于抽象而不依赖于实现。策略是个好的手段,能用好才能发挥出它更大的威力来。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值