SFINAE

SFINAE 是 C++ 中的一种语言规则,其大意是:编译器在尝试将模版形参替换为模板实参的时候,如果替换后得到的结果不是合法的代码(替换失败),编译器不会报错,而仅仅是忽略它。

SFINAE

1
2
3
4
template<typename T>
bool equal(const T& a, const T& b) {
return a == b;
}

如果是 int 类型的数据这里不会出错,但是 double 浮点型就有问题,因为浮点数无法判断是否相等。

也许你想到特化模板,对浮点数特别处理,比方说:

1
2
3
4
5
template<>
bool equal<double>(const T& a, const T& b) {
cout << "无法处理 double" << endl;
return true;
}

但如果我们用 SFINAE 规则会如何处理?

1
2
3
4
5
6
7
8
9
#include <type_traits>

// 借助 SFINAE 对 T 进行约束
template<typename T,
typename = std::enable_if_t<std::is_integral_v<T>>
>
bool equal(const T& a, const T& b) {
return a == b;
}

std::enable_if<条件>::type 用于启用或禁用模板。而代码中的std::enable_if_t<条件>std::enable_if<条件>::type 的简写。

std::is_integral<T>::value 是 C++ 类型特征的一部分,如果是 int 类型返回 true,如果不是就返回 false。而代码中的std::is_integral_v<T>std::is_integral<T>::value 的简写。

SFINAE 的替代品:C++20 Concepts

1
2
3
4
5
template<typename T>
requires std::is_integral_v<T>
bool equal(const T &a, const T &b) {
return a == b;
}

更简洁的写法:

1
2
3
4
template<std::integral T>
bool equal(const T &a, const T &b) {
return a == b;
}

如果你要限制多个不同的类型,怎么办?通过 || 来连接多个限制。

1
2
3
4
5
6
7
#include <concepts>

template <typename T>
requires (std::integral<T> || std::floating_point<T>)
bool equal(const T& a, const T& b) {
return a == b;
}

你还可以这么做:

1
2
3
4
5
6
7
template <typename T>
concept Number = std::integral<T> || std::floating_point<T>;

template <Number T>
bool equal(const T& a, const T& b) {
return a == b;
}

参考文章

从 SFINAE 到 C++20 Concepts