enable_if
SFINAE 是 substitution failure is not an error 的缩写,即匹配失败不是错误。就是说,匹配重载的函数 / 类时如果匹配后会引发编译错误,这个函数 / 类就不会作为候选。这是一个 C++11 的新特性,也是 enable_if 最核心的原理。
头文件:
#include <type_traits> template<bool B, class T = void> struct enable_if;
这个模板实现相当简单,看一下一个版本的实现:
template<bool B, class T = void> struct enable_if {}; template<class T> struct enable_if<true, T> { typedef T type; };
第一个普通版本的模板类定义,第二个为偏特化版本的模板类定义。它在第一个模板参数为false的时候并不会定义type,只有在第一模板参数为true的时候才会定义type。
1. 示例
1 #include <iostream> 2 #include <type_traits> 3 using namespace std; 4 5 template<int a, int b> 6 typename enable_if <a + b == 233, bool>::type is233() { 7 return true; 8 } 9 10 template<int a, int b> 11 typename enable_if <a + b != 233, bool>::type is233() { 12 return false; 13 } 14 15 int main() { 16 cout << is233<1, 232>() << endl;//true 17 cout << is233<114514, 1919>() << endl; //false 18 return 0; 19 }
2. 限制模板函数的参数类型
在某些场景下,我们需要实现只有特定类型可以调用的模板函数。如下代码所示,通过对返回值使用std::enable_if和在模板参数中使用std::enable_if均实现了只允许整形参数调用函数的功能。
1 #include <iostream> 2 #include <type_traits> 3 4 template <class T> 5 typename std::enable_if<std::is_integral<T>::value, bool>::type 6 is_odd (T i) { 7 return bool(i % 2); 8 } 9 10 template < class T, 11 class = typename std::enable_if<std::is_integral<T>::value>::type> 12 bool is_even (T i) { 13 return !bool(i % 2); 14 } 15 16 int main() { 17 short int i = 1; 18 19 std::cout << std::boolalpha; 20 std::cout << "i is odd: " << is_odd(i) << std::endl; 21 std::cout << "i is even: " << is_even(i) << std::endl; 22 23 return 0; 24 }
输出:
i is odd: true i is even: false
3.模板类型偏特化
在使用模板编程时,可以利用std::enable_if的特性根据模板参数的不同特性进行不同的类型选择。如下所示,我们可以实现一个检测变量是否为智能指针的实现:
1 #include <iostream> 2 #include <type_traits> 3 #include <memory> 4 5 template <typename T> 6 struct is_smart_pointer_helper : public std::false_type {}; 7 8 template <typename T> 9 struct is_smart_pointer_helper<std::shared_ptr<T>> : public std::true_type {}; 10 11 template <typename T> 12 struct is_smart_pointer_helper<std::unique_ptr<T>> : public std::true_type {}; 13 14 template <typename T> 15 struct is_smart_pointer_helper<std::weak_ptr<T>> : public std::true_type {}; 16 17 template <typename T> 18 struct is_smart_pointer : public is_smart_pointer_helper<typename std::remove_cv<T>::type> {}; 19 20 template <typename T> 21 typename std::enable_if<is_smart_pointer<T>::value, void>::type check_smart_pointer(const T &t) { 22 std::cout << "is smart pointer" << std::endl; 23 } 24 25 template <typename T> 26 typename std::enable_if < !is_smart_pointer<T>::value, void >::type check_smart_pointer(const T &t) { 27 std::cout << "not smart pointer" << std::endl; 28 } 29 30 int main() { 31 int *p(new int(2)); 32 std::shared_ptr<int> pp(new int(2)); 33 std::unique_ptr<int> upp(new int(4)); 34 35 check_smart_pointer(p); 36 check_smart_pointer(pp); 37 check_smart_pointer(upp); 38 39 return 0; 40 }