【C++编程】std::enable_if 和 SFINAE
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. 示例
#include <iostream>
#include <type_traits>
using namespace std;
template<int a, int b>
typename enable_if <a + b == 233, bool>::type is233() {
return true;
}
template<int a, int b>
typename enable_if <a + b != 233, bool>::type is233() {
return false;
}
int main() {
cout << is233<1, 232>() << endl;//true
cout << is233<114514, 1919>() << endl; //false
return 0;
}
2. 限制模板函数的参数类型
在某些场景下,我们需要实现只有特定类型可以调用的模板函数。如下代码所示,通过对返回值使用std::enable_if和在模板参数中使用std::enable_if均实现了只允许整形参数调用函数的功能。
#include <iostream>
#include <type_traits>
template <class T>
typename std::enable_if<std::is_integral<T>::value, bool>::type
is_odd (T i) {
return bool(i % 2);
}
template < class T,
class = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even (T i) {
return !bool(i % 2);
}
int main() {
short int i = 1;
std::cout << std::boolalpha;
std::cout << "i is odd: " << is_odd(i) << std::endl;
std::cout << "i is even: " << is_even(i) << std::endl;
return 0;
}
输出:
i is odd: true i is even: false
3.模板类型偏特化
在使用模板编程时,可以利用std::enable_if的特性根据模板参数的不同特性进行不同的类型选择。如下所示,我们可以实现一个检测变量是否为智能指针的实现:
#include <iostream>
#include <type_traits>
#include <memory>
template <typename T>
struct is_smart_pointer_helper : public std::false_type {};
template <typename T>
struct is_smart_pointer_helper<std::shared_ptr<T>> : public std::true_type {};
template <typename T>
struct is_smart_pointer_helper<std::unique_ptr<T>> : public std::true_type {};
template <typename T>
struct is_smart_pointer_helper<std::weak_ptr<T>> : public std::true_type {};
template <typename T>
struct is_smart_pointer : public is_smart_pointer_helper<typename std::remove_cv<T>::type> {};
template <typename T>
typename std::enable_if<is_smart_pointer<T>::value, void>::type check_smart_pointer(const T &t) {
std::cout << "is smart pointer" << std::endl;
}
template <typename T>
typename std::enable_if < !is_smart_pointer<T>::value, void >::type check_smart_pointer(const T &t) {
std::cout << "not smart pointer" << std::endl;
}
int main() {
int *p(new int(2));
std::shared_ptr<int> pp(new int(2));
std::unique_ptr<int> upp(new int(4));
check_smart_pointer(p);
check_smart_pointer(pp);
check_smart_pointer(upp);
return 0;
}