SFINAE
SFINAE全称:Substitution Failure is not an error.即在对模板函数调用进行实例化推导匹配时出现的无效函数将被删除,并且编译器不会报错,只有最终有一个可以匹配该次调用的实例化match即可。
在对一个函数调用进行模板推导时,编译器会尝试推导所有的候选函数(模板、重载函数、普通函数),并最终结合偏特化原则,给出一个最好的匹配结果。如果在推导的过程中,出现了无效的模板参数等情况,并不会报错,而是将该实例化函数从候选集先行删除。
struct Test {
typedef int foo;
};
template<typename T>
void func(typename T::foo) { } //第一个模板定义
template<typename T>
void func(T) { } //第二个模板定义
int main() {
func<Test>(10); //调用#1, 根据模板推导原则,其实也会推导func(Test )的情况,但是因为SFINAE机制,不会报错;
func<int>(10); //调用#2, 其实编译器也会实例出int::foo,这个类型不存在,但是因为SFINAE机制,不会报错;
}
std::enable_if<>
enable_if在标准库中通过结构体模板来实现,声明如下:
template<bool condition, class T = void> struct enable_if;
Enable type if the condition is met.
The type T is enabled as member type enable_if::type if Cond is true. Otherwise, enable_if::type is not defined.This is useful to hide signatures on compile time when a particular condition is not met, since in this case, the member enable_if::type will not be defined and attempting to compile using it should fail.
如果condition满足,则会声明一个类型T,如果 condition 不满足的话,会生成一个无效的类型,此处由于 SFINAE 机制的存在,只要 call 存在一个匹配的话,就不会报错(只是简单的丢弃该函数)。
std::enable_if<>
的实现机制如下代码所示:
template<bool Cond, typename T = void> struct enable_if {};
template<typename T> struct enable_if<true, T> { typedef T type; };
在 condition 为真的时候,由于偏特化机制,第二个结构体模板明显是一个更好的匹配,所以 std::enable_if<>::type
就是有效的。当 condition 为假的时候,只有第一个结构体模板能够匹配,所以 std::enable_if<>::type
是无效的,会被丢弃,也不会因为enable_if
的使用导致报错。
可以根据C++官网的enable_if
使用demo来观看使用方式
// enable_if example: two ways of using enable_if
#include <iostream>
#include <type_traits>
// 1. the return type (bool) is only valid if T is an integral type:
//将enable_if用在返回值的类型定义上,可以参考返回值类型追踪机制,异曲同工,不过这里只有在T是int时,返回值才
//是bool,否则实例化的函数无效
template <class T>
typename std::enable_if<std::is_integral<T>::value,bool>::type
is_odd (T i) {return bool(i%2);}
// 2. the second template argument is only valid if T is an integral type:
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; // code does not compile if type of i is not integral
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;
}
OUTPUT
i is odd: true
i is even: false