在泛型技术中,类型参数化实现了泛的含义,那么在具体使用中,光有泛是不行的,同样一个泛型函数中,基本类型能和自定义类型完全一致吗,指针能和具体类型一致吗,常量引用和常量指针呢,代码总要落在实处,类型总要具体化,怎么办?我们又不能总是特化,特化太多,模板就失去意义。
在我们面向对象的设计中,我们针对接口编程,用具体的类的对象实现了特例,移动这个接口,2条腿和4条腿以及长翅膀的都有不同实现。
那么在泛型技术中,答案就是Trait技术,Policy技术
类有属性(即数据)和操作两个方面。同样模板也有自己的属性,类型正是模板属性之一
举个例子,在 iostream库中,streambuf的接口依赖EOF的值,在传统的库中,EOF是int
class streambuf {
...
int sgetc(); // return the next character, or EOF.
int sgetn(char*, int N); // get N characters.
};
如果我们想把它泛型话,我不仅要对char泛型话,还要针对EOF
template <class charT, class intT>
class basic_streambuf {
...
intT sgetc();
int sgetn(charT*, int N);
};
这样问题就来了,用户其实不用该关心EOF。我们要想办法把多余的参数化类型去掉,这就是我们所说的Trait的用武之地。
我们先定义一个模板
template <class charT>
struct ios_char_traits { };
我们先定义个空实现作为默认的,针对具体的字符类型,我们可以特化以提供有用的语义。
struct ios_char_traits<char> {
typedef char char_type;
typedef int int_type;
static inline int_type eof() { return EOF; }
};
现在,我们的 basic_streambuf 是这个样子的
template <class charT>
class basic_streambuf {
public:
typedef ios_char_traits<charT> traits_type;
typedef traits_type::int_type int_type;
int_type eof() { return traits_type::eof(); }
...
int_type sgetc();
int sgetn(charT*, int N);
};
这样我们的新模板类消除了没有用的模板参数,其他的部分和前面都很类似,编译器自己在Traits中
寻找类型
如果这个流要支持新的类型,我们只要定义一个新的ios_char_traits,
struct ios_char_traits<wchar_t> {
typedef wchar_t char_type;
typedef wint_t int_type;
static inline int_type eof() { return WEOF; }
};
trait在把基本类型传人模板和一些不支持模板的某些调用的类型时特别有用,这也实现了一个面向对象设计原则中的一条,对扩展开放,对修改关闭。你肯定没发对基本类型做修改,也无法对有限制修改的库做修改,这时你就该考虑,trait
使用triat技术在我们一个数值分析库中,我们可能这样支持泛型话
template <class numT>
struct float_traits { };
struct float_traits<float> {
typedef float float_type;
enum { max_exponent = FLT_MAX_EXP };
static inline float_type epsilon() { return FLT_EPSILON; }
...
};
struct float_traits<double> {
typedef double float_type;
enum { max_exponent = DBL_MAX_EXP };
static inline float_type epsilon() { return DBL_EPSILON; }
...
};
这样我们可以在不知道具体类型是float还是double或者是自定义类型的情况下使用 "max_exponent"。
template <class numT>
class matrix {
public:
typedef numT num_type;
typedef float_traits<num_type> traits_type;
inline num_type epsilon() { return traits_type::epsilon(); }
...
};