一、需求引入
到目前为止,我们一直都将累计(accumulation)与求和(summation)混为一谈。显然我们其实可以设想其它种类的累计。例如我们可以求给定之实值序列的乘积;如果被操作的实值是字串,我们可以将它们串接起来;甚至「寻找序列中的最大值」也可被归结为累计问题。在所有情况中,accum()惟一需要修改的就是 total += *beg。这个操作可被称为「累计运算」过程中的一个 policy(策略)。因此所谓 policy class 就是这样的 class:提供一个接口,从而得以在算法中运用一或多个 policies。
二、实现
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.0;
}
};
//普通的求和代理类
class SumPolicy {
public:
template<typename T1, typename T2>
static void accumulate (T1& total, T2 const & value) {
total += value;
}
};
//普通的求积代理类
class MultPolicy {
public:
template<typename T1, typename T2>
static void accumulate (T1& total, T2 const& value) {
total *= value;
}
};
//trait 和 policy的联合使用 模板
template <typename T, typename Policy = SumPolicy,
typename Traits = AccumulationTraits<T> >
class Accum {
public:
//定义trait的类型
typedef typename Traits::AccT AccT;
//累积函数
static AccT accum (T const* beg, T const* end) {
//初始化,注意使用MultPolicy 时,不能为 0
AccT total = Traits::zero();
while (beg != end) {
//调用普通代理类的成员函数模板
Policy::accumulate(total, *beg);
++beg;
}
return total;
}
};
三、使用
int main() {
// 产生一个 array,内有 5 个整数值
int num[] = { 1, 2, 3, 4, 5 };
// 打印所有数值的乘积
std::cout << "the product of the integer values is "
<< Accum<int,MultPolicy>::accum(&num[0], &num[5])
<< '\n';
}
然而上述程序的输出结果不如预期:
the product of the integer values is 0
问题在于我们对初始值的不当选择:初始值 0 对「求和」而言很合适,对「乘积」来说就不妥 了(初始值为 0,注定乘积结果也是 0)。这也说明了不同的 traits 和 policies 可能相互影响,同时也强调了谨慎设计 templates 的重要性。
从这个例子中我们可以意识到,accumulation loop(累计循环)的初始化应该成为 accumulation policy 的一个成份。这个 policy 可以使用也可以不使用 trait zero()。其它替代方案也不该被遗忘 ― 并不是什么事都非得使用 traits 和 policies 解决不可。C++ 标准库的accumulate()就是接受第三个 call argument 做为初始值