跟我学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
static void accumulate (T1& total, T2 const & value) {
total += value;
}
};

2、抽象为模板的Policy

template 
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 template
class AccumulationTraits;template<>
class AccumulationTraits
{
public:typedef int AccT;static AccT zero(){return 0;}
};template<>
class AccumulationTraits
{
public:typedef int AccT;static AccT zero(){return 0;}
};template<>
class AccumulationTraits
{
public:typedef long AccT;static AccT zero(){return 0;}
};template<>
class AccumulationTraits
{
public:typedef unsigned long AccT;static AccT zero(){return 0;}
};template<>
class AccumulationTraits
{
public:typedef double AccT;static AccT zero(){return 0;}
};
//...class SumPolicy
{
public:templatestatic void accumulate(T1& total, T2 const& value){total += value;}
};template  >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:templatestatic void accumulate(T1& total, T2 const& value){total * = value;}
};void TestAccum()
{int num[] = {1,2,3,4,5};std::cout << "all value is:" << Accum::accum(&num[0], &num[5],0) << std::endl;std::cout << "all mult value is:" << Accum::accum(&num[0], &num[5]) << std::endl;
}
int  main()
{TestAccum();return 0;
}

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

//模板类
#include 
template 
class SumPolicy {
public:static void accumulate(T1& total, T2 const& value) {total += value;}
};template
class AccumulationTraits;template<>
class AccumulationTraits
{
public:typedef int AccT;static AccT zero(){return 0;}
};template<>
class AccumulationTraits
{
public:typedef int AccT;static AccT zero(){return 0;}
};template<>
class AccumulationTraits
{
public:typedef long AccT;static AccT zero(){return 0;}
};template<>
class AccumulationTraits
{
public:typedef unsigned long AccT;static AccT zero(){return 0;}
};template<>
class AccumulationTraits
{
public:typedef double AccT;static AccT zero(){return 0;}
};template
class MultPolicy {
public:static void accumulate(T1& total, T2 const& value) {total * = value;}
};template  class Policy = SumPolicy,typename Traits = AccumulationTraits >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;}
};
void TestTAccum()
{// create array of 5 integer valuesint num[] = { 1, 2, 3, 4, 5 };// print average valuestd::cout << "the average value of the integer values is "<< Accum::accum(&num[0], &num[5]) / 5<< '\n';// print product of all valuesstd::cout << "the product of the integer values is "<< Accum::accum(&num[0], &num[5])<< '\n';// create array of character valueschar name[] = "templates";int length = sizeof(name) - 1;// (try to) print average character valuestd::cout << "the average value of the characters in \""<< name << "\" is "<< Accum::accum(&name[0], &name[length]) / length<< '\n';
}
int  main()
{TestTAccum();return 0;
}

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

template
class SumPolicy {
public:templatestatic void accumulate(T1& total, T2 const& value) {total += value;}
};template<>
class SumPolicy {
public:templatestatic void accumulate(T1& total, T2 const& value) {total = total + value;}
};
template ,typename Traits = AccumulationTraits >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,再为设计所使用,其实就是设计的依赖于抽象而不依赖于实现。策略是个好的手段,能用好才能发挥出它更大的威力来。


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部