C++ traits和enable_if的实现

来源:互联网 发布:python 技术指标 编辑:程序博客网 时间:2024/04/30 10:53

C++ traits中的trait在中文里好像没有特别好的翻译。引用C++之父Bjarne Stroustrup的话来解释什么是C++ traits:

"Think of a trait as a small object whose main purpose is to carry information used by another object or algorithm to determine "policy" or "implementation details."

翻译过来就是:trait是一个很小的对象。它的主要作用是携带信息,以便其他的对象或者算法可以使用它来确定策略或者实现细节。

这么说起来还是不太好理解,举个例子大概会明白一些。我们构造一个trait叫做is_int_long:

template <typename T>struct is_long_or_int {    static const bool value = false;};template <>struct is_long_or_int<int> {    static const bool value = true;};template <>struct is_long_or_int<long> {    static const bool value = true;};

上面定义了一个模板类,叫做is_long_or_int<T>(如果这时候你在想这个定义的是一个结构而不是一个类,建议你去复习一下C++,弄清楚class和struct两个关键字的区别)。这个模板类里面有个静态的常量value,它的值是false。然后我们将这个模板类特殊化了两次,分别使得模板参数为int或者long的时候那个静态常量value的值为true,也就是:

is_long_or_int<int>::value == true;is_long_or_int<long>::value == true;is_long_or_int<T>::value == false; //其他所有的类型
这个模板类is_long_or_int就是一个trait,它携带了给定类T是否是int或者long的信息。

这看起来很无聊,又有什么用呢?其实用处很大。例如我有一个模板函数:

template <typename T>bool larger_than_five(T a) {    return a > (T)5;}
这是一个很简单的函数,只是为了用来为解释trait的作用服务,请不要纠结其中实现的细节。这个函数中用了大于运算符(>),并且和5来比较,并不是所有的这样的比较都有意义。比如说:

void *p = NULL;larger_than_five(p); //毫无意义
不但毫无意义,更可能是一个编程的错误。最好的解决办法是:只允许使用int和long来实例化这个模板函数,而不允许其他任何类型。当然可以是unsigned int或者unsigned long,为了简洁,我们这里只允许int和long。可是,C++的核心语言标准中并没有提供这样的语法。然而,使用C++ trait就能很好的解决这个问题。

C++11标准库中有个模板类叫做enable_if,正是用来实现这种功能的。我们只需要把上述模板函数larger_than_five改写成如下这个样子:

template <typename T>typename enable_if<larger_than_five<T>::value, bool>::typelarger_than_five(T a) {    return a > (T)5;}

如果看上去很复杂,不用着急,容我来一点点地解释。首先,要了解enable_if的实现,大概是如下这个样子,当然模板库里面的会有很多不同:

template <bool b, typename T>struct enable_if {};template <typename T>struct enable_if<true, T> {    typedef T type;};
我们可以看到enable_if其实是个模板类,当且仅当它的第一个参数的值为true的时候,在类中typedef定义一个type的类,类型与T相同。看完了这个,我们在看上面那个larger_than_five的模板函数。其中,enable_if中的b = larger_than_five<T>::value。如果T = int 或者long,larger_than_five<T>::value的值是true,那么enable_if中的type就定义为T,所以larger_than_five模板函数就等价为:

template <typename T>bool larger_than_five(T a) {    return a > (T)5;}
和我们看到它的第一个版本一模一样!如果我们给的类型不是int或者long,比如说void *,那么enable_if中就没有type这个定义,所以larger_than_five这个模板函数就变成了:

template <typename T> larger_than_five(T a) {    return a > (T)5;}

编译器就会给出error,程序便无法编译。


0 0