Traits技术以一个统一的编程接口,描述各种数据类型的基本特征。例如,对于基本类型float,如果将这种类型所能表示的最大浮点数写为以2为底的指数形式,其指数部分的最大值为128。在float.h中这个常量被定义为FLT_MAX_EXP。而对于基本类型double,这个最大值为1024,被定义为DBL_MAX_EXP。类似地,float和double所能够表示的最小正数也不一致,分别被定义为常量FLT_EPSILON以及DBL_EPSILON。设想我们要设计一个数值分析库,待处理的数值可以被表示为float、double或者long double。由于这几种类型具有不同的指数部分最大值、最小正数等特征,如果不采用traits技术,程序的多个地方需要查询数值的类型并依据该类型的特征做相应的处理。使用traits技术,不同数据类型的特征被封装在一个类模板中,程序其他模块可以使用这个类模板的接口,获得每个数据类型的特征信息。
// traits.h
#ifndef TRAITS_H
#define TRAITS_H
#define FLT_MAX_EXP (128)
#define FLT_MAX_EPSILON (0)
#define DBL_MAX_EXP (1024)
#define DBL_EPSILON (1)
// 主类模板
template <typename numT>
struct fp_traits
{
};
// 特化模板 =》float
template <>
struct fp_traits<float>
{
typedef float fp_type;
enum{ max_exponent = FLT_MAX_EXP};
static inline fp_type epsilon(){
return FLT_MAX_EPSILON;
}
};
// 特化模板=》double
template <>
struct fp_traits<double>
{
typedef double fp_type;
enum { max_exponent = DBL_MAX_EXP};
static inline fp_type epsilon(){
return DBL_EPSILON;
}
};
// 上层接口
template <typename numT>
class matrix{
public:
typedef numT num_type;
typedef fp_traits<num_type> num_type_info;
inline num_type epsilon(){
return num_type_info::epsilon();
}
};
#endif // TRAITS_H
// main.cpp
#include <QCoreApplication>
#include "traits.h"
#include <iostream>
using namespace std;
int main(int argc, char *argv[])
{
QCoreApplication a(argc, argv);
// traits 技术封装 ==>给上层软件模块—— 类模板
// matrix提供统一的编程接口
matrix<float> fm;
matrix<double> dm;
cout << "float matrix:" << fm.epsilon() << endl;
cout << "double matrix:" << dm.epsilon() << endl;
return a.exec();
}
这两个特化的类模板都向上层软件提供了统一的编程接口:类型名fp_type表示其类型,枚举常量max_exponent表示最大指数值,而静态成员函数epsilon()返回最小正数。而上层软件模块——类模板matrix通过这个统一的接口获取类型的信息。这样,各种数据类型的差异性被封装在类模板中,减少了上层软件模板对这个差异性的依赖性,降低了软件模块之间的耦合度。相当精彩的技巧!!!