这里实现一个通用的计算数组类的数据求和函数。C++中用模板实现多态属于静态多态,trait类就是做为模板参数以实现静态多态。根据不同的模板参数类型,以实现不同模板数据类型所需的功能。其实说白了,就是利用模板的一些基本规则,将其合理的组合起来,以达到根据类型不同时函数的操作有细微个性的差别。
依据TDD开发模式,先写测试用例。
#include <iostream>
#include <assert.h>
#include "accum.hpp"
using namespace std;
int main(int argc, char **argv)
{
int arr[5] = {1, 2, 3, 4, 5};
cout << accum(arr, arr+5) << endl;
assert(accum(arr, arr+5) == 15);
return 0;
}
1、没有使用trait的实现方式
template <typename T>
inline T& accum(T const* beg, T const* end)
{
T total = T(); //初始化,假定初始值为0
while (beg != end)
{
total += *beg;
++beg;
}
return total;
}
上述代码的缺点:1、初始化不一定是初始值为0;2、T类型求和可能导致类型溢出。
2、解决类型溢出问题,采用类型关联方式实现
//通过特化形式,将类型关联起来,以扩大类型的范围
template<typename T>
struct AccumulateTraits; //trait模板
template<> //模板特化,类型关联
struct AccumulateTraits<char>
{
typedef int AccT;
};
template<>
struct AccumulateTraits<short>
{
typedef int AccT;
};
template<>
struct AccumulateTraits<int>
{
typedef long AccT;
};
template<>
struct AccumulateTraits<unsigned int>
{
typedef unsigned long AccT;
};
template<>
struct AccumulateTraits<float>
{
typedef double AccT;
};
#include "traits.hpp"
template<typename T>
inline typename AccumulateTraits<T>::AccT accum(T const* beg, T const* end)
{
//使用模板类型中的类型时,一定要typename修饰
typedef typename AccumulateTraits<T>::AccT AccT;
AccT total = AccT();
while (beg != end)
{
total += *beg;
++beg;
}
return total;
}
3、在2的基础上解决初始化不为0问题(value trait——值萃取)
template<typename T>
struct AccumulateTraits; //trait模板
template<> //模板特化,类型关联
struct AccumulateTraits<char>
{
typedef int AccT;
//通过静态成员函数来初始化为零
static AccT zero()
{
return 0;
}
};
template<>
struct AccumulateTraits<short>
{
typedef int AccT;
static AccT zero()
{
return 0;
}
};
template<>
struct AccumulateTraits<int>
{
typedef long AccT;
static AccT zero()
{
return 0;
}
};
template<>
struct AccumulateTraits<unsigned int>
{
typedef unsigned long AccT;
static AccT zero()
{
return 0;
}
};
template<>
struct AccumulateTraits<float>
{
typedef double AccT;
static AccT zero()
{
return 0.0;
}
};
#include "traits.hpp"
template<typename T>
inline typename AccumulateTraits<T>::AccT accum(T const* beg, T const* end)
{
typedef typename AccumulateTraits<T>::AccT AccT;
AccT total = AccumulateTraits<T>::zero();
while (beg != end)
{
total += *beg;
++beg;
}
return total;
}
上述代码的缺点是:类型绑定过死板,已经绑定不能被修改,有时也需要对类型进行自行定制。
4、采用参数化的形式进行类型萃取(trait)
template<typename T, typename AT = AccumulateTraits<T> >
struct Accum
{
typedef typename AT::AccT AccT;
static AccT accum(T const* beg, T const* end)
{
AccT total = AT::zero();
while (beg != end)
{
total += *beg;
++beg;
}
return total;
}
};
//test case
#include <iostream>
#include <assert.h>
#include "accum.hpp"
using namespace std;
int main(int argc, char **argv)
{
int arr[5] = {1, 2, 3, 4, 5};
assert(Accum<int>::accum(arr, arr+5) == 15);
return 0;
}
总之,类型萃取(trait)在处理类型关联有大的作用。