std::enable_if
顾名思义,满足条件时类型有效。作为选择类型的小工具,其广泛的应用在 C++ 的模板元编程中。它的定义也非常的简单:
// STRUCT TEMPLATE enable_if
template <bool _Test, class _Ty = void>
struct enable_if {
}; // no member "type" when !_Test
template <class _Ty>
struct enable_if<true, _Ty> {
// type is _Ty for _Test
using type = _Ty;
};
由此可知,只有当第一个模板参数_Test
为 true
时,type
才有定义(type
即第二个模板参数_Ty
);否则使用 type
会产生编译错误,且默认模板参数可以让你不必指定类型。
另外enable_if_t
是一个别名,其定义如下:
template <bool _Test, class _Ty = void>
using enable_if_t = typename enable_if<_Test, _Ty>::type;
std::enable_if
可以用作函数参数、返回类型或类模板或函数模板参数,以有条件地从重载中删除函数或类。
一、作为函数参数
template<typename T>
struct Check1
{
//如果T的类型是int,则定义函数 int read(void* = nullptr)
template<typename U = T>
U read(typename std::enable_if_t<std::is_same_v<U, int> >* = nullptr) {
return 42;
}
//如果T的类型是double,则定义函数 double read(void* = nullptr)
template<typename U = T>
U read(typename std::enable_if_t<std::is_same_v<U, double> >* = nullptr) {
return 3.14;
}
};
使用示例
Check1<int> c1;
int x1 = c1.read();
Check1<double> c2;
double x2 = c2.read(0);
二、作为模板参数
template<typename T>
struct Check2
{
//如果T的类型是int,则定义函数 int read()
template<typename U = T, typename std::enable_if_t<std::is_same_v<U, int>, int> = 0>
U read() {
return 42;
}
//如果T的类型是double,则定义函数 double read()
template<typename U = T, typename std::enable_if_t<std::is_same_v<U, double>, int> = 0>
U read() {
return 3.14;
}
};
也可以写成如下形式:
template<typename T>
struct Check2
{
template<typename U = T, typename std::enable_if_t<std::is_same_v<U, int>>* = nullptr>
U read() {
return 42;
}
template<typename U = T, typename std::enable_if_t<std::is_same_v<U, double>>* = nullptr>
U read() {
return 3.14;
}
};
三、作为返回值类型
对于模板函数,有时希望根据不同的模板参数返回不同类型的值。
template<typename T>
struct Check3
{
//如果T的类型是int,则定义函数 int read()
template<typename U = T>
typename std::enable_if_t<std::is_same_v<U, int>, U> read() {
return 42;
}
//如果T的类型是double,则定义函数 double read()
template<typename U = T>
typename std::enable_if_t<std::is_same_v<U, double>, U> read() {
return 3.14;
}
};
利用auto关键字,可以写为如下形式:
// int goo<true>(int x)
template<bool B>
auto goo(int x) -> std::enable_if_t<B, int>
{
return 1;
}
// int goo<false>(int x)
template<bool B>
auto goo(int x) -> std::enable_if_t<!B, int>
{
return 2;
}
四、类型偏特化
在使用模板编程时,经常会用到根据模板参数的某些特性进行不同类型的选择,或者在编译时校验模板参数的某些特性。例如:
// T是其它类型
template<typename T, typename = void>
struct zoo;
// 如果T是浮点类型
template<typename T>
struct zoo<T, std::enable_if_t<std::is_integral_v<T>>>
{
};
五、concepts
C++20中利用concepts可以简化编写方式:
#ifdef __cpp_lib_concepts
#include <concepts>
#endif
// 如果T是整数类型
template<std::integral T>
void display_concepts_1(T num)
{
}
// 如果T是整数类型
void display_concepts(std::integral auto num)
{
}
// 如果T是浮点数类型
void display_concepts(std::floating_point auto num)
{
}
等价于如下形式:
// 如果T是整数类型
template<typename T, typename std::enable_if_t<std::is_integral_v<T>>* = nullptr>
void display_1(T num)
{
}
// 如果T是整数类型
template<typename T>
void display(typename std::enable_if_t<std::is_integral_v<T>>* = nullptr)
{
}
// 如果T是浮点数类型
template<typename T>
void display(typename std::enable_if_t<std::is_floating_point_v<T>>* = nullptr)
{
}