boost::enable_if boost::lazy_enable_if 模板函数的重载遵循SFINAE原则(substitution-failure-is-not-an-error):当一个模板函数的返回值或参数类型无效的时候,该实例不会参与重载解析,也不会导致编译错误。 利用这个原则,可以设计出具有选择性的模板函数。 先看一个例子: int negate(int i) { return -i; } //1 template <class F> typename F::result_type negate(const F& f) { return -f(); } //2 当编译器遇到negate(1)的调用的时候,非模板函数1完全匹配,但是模板函数2也会尝试去实例化。显然无法用int去实例化模板函数2,这里如果没有SFINAE原则就会导致编译错误。实际上用int实例化的模板函数2不会出现在重载决议列表中,可能这个实例直接被删掉了。 boost::enable_if就是用来将某些不合要求的模板函数从重载决议列表中踢掉。 template <bool B, class T = void> struct enable_if_c { typedef T type; }; template <class T> struct enable_if_c<false, T> {}; template <class Cond, class T = void> struct enable_if : public enable_if_c<Cond::value, T> {}; 可以看到enable_if实际上继承自enable_if_c,而模板enable_if_c需要两个模板参数 第一个是bool类型,来自enable_if的第一个参数的bool成员value,第二个参数是用户提供的类型T。 enable_if_c的作用就是如果第一个模板参数为true,那么它就将第二个模板参数typedef为type,否则什么也不干。那么enable_if_c<true, T>::type就是T,而enable_if_c<false, T>::type就是错误 template <class T> typename enable_if_c<boost::is_arithmetic<T>::value, T>::type foo(T t) { return t; } 上面这个函数foo只接受算术类型参数 使用enable_if可以写得更简单一些: template <class T> typename enable_if<boost::is_arithmetic<T>, T>::type foo(T t) { return t; } 注意: enable_if第一个模板参数必须是定义了bool成员value的一个类类型Cond 第二个参数为任意类型T enable_if<true, T>::type为T enable_if<false, T>::type无效 注意SFINAE只适用于模板函数,所以不能用于模板类的普通成员函数, 可以用于模板函数和模板类的模板成员函数。 从上面可以看出:不管enable_if_c的第一个参数是true还是false,第二个参数类型必须正确。但是有一种需求是这样的:第一个参数为true的时候,用户保证第二个参数有效,而第一个参数为false的时候用户不保证有效。这个时候就无法使用enable_if了,因为这时解析enable_if模板就已经失败了,这已经不是SFINAE的范畴的 例如: template <class T, class U> class mult_traits; template <class T, class U> typename enable_if<is_multipliable<T, U>, typename mult_traits<T, U>::type>::type operator*(const T& t, const U& u) { ... } 对于某些类型T和U的mult_traits特化版本,用户定义了类型成员type,其它情况没有 那么对于“其它情况”,mul_traits<T, U>::type就无效,导致解析enable_if模板失败,所以enable_if就实现不了这个功能了 lazy_enable_if可以用来解决这个问题 lazy_enable_if也接受两个模板参数: 第一个与enable_if一样,是一个定义bool成员value的一个类类型Cond。 第二个参数是一个有效类型T,并且当Cond::value是true的时候, 由用户保证typedef过T::type,此时lazy_enable_if这个模板类定义类型成员 typedef T::type type, 当Cond::value是false时不要求用户定义T::type,同时lazy_enable_if也啥都不定义, 这样一来就避免了在解析lazy_enable_if这个模板的时候遇到错误, 而是在求lazy_enable_if<false, T>::type的时候出现错误 正确代码如下: template <class T, class U> class mult_traits; template <class T, class U> typename lazy_enable_if<is_multipliable<T, U>, typename mult_traits<T, U> >::type operator*(const T& t, const U& u) { ... } 注意: lazy_enable_if接受第一个模板参数是一个定义bool成员value的一个类类型Cond, 当Cond::value为true时第二个参数必须是定义类型成员type的类类型 当Cond::value为false时第二个参数为任意类型。 lazy_enable_if<true, T>::type为T::type lazy_enable_if<false, T>::type无效 |
|