C++笔记-模板类的类型限定: enable_if、static_assert
0. 参考资料:
静态断言static_assert: https://www.cnblogs.com/Braveliu/p/12220769.html
std::enable_if的几种用法: https://blog.csdn.net/jeffasd/article/details/84667090 | https://yixinglu.gitlab.io/enable_if.html
1. 静态断言语法:
static_assert(常量表达式,"提示字符串")
使用static_assert,可以在编译期发现更多的错误,用编译器来强制保证一些契约,帮助我们改善编译信息的可读性,尤其是用于模板时。
注意:
[1]使用范围:static_assert可以用在全局作用域中,命名空间中,类作用域中,函数作用域中,几乎可以不受限制的使用。
[2]常量表达式:static_assert的断言表达式的结果必须是在编译时期可以计算的表达式,即必须是常量表达式,示例如下:
//确保模板类的入参类型为integer类
template <typename T>
T add_2(T t) {
static_assert(std::is_integral_v<T>, "Type T should be integer");
return t+1;
}
错误用法:
int positive(const int n) {
static_assert(n > 0, "value must > 0");
return 0;
}
2. std::enable
官方解释
Enable type if 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.
It is defined with a behavior equivalent to:
template<bool Cond, class T = void> struct enable_if {};
template<class T> struct enable_if<true, T> { typedef T type; }; // 这里利用了上一行定义的struct
// 只有当第一个模板参数为 true 时,type 才有定义,否则使用 type 会产生编译错误,并且默认模板参数可以让你不必指定类型。
用法一:类型偏特化
在使用模板编程时,经常会用到根据模板参数的某些特性进行不同类型的选择,或者在编译时校验模板参数的某些特性。
用法二:控制函数返回类型
对于模板函数,有时希望根据不同的模板参数返回不同类型的值,进而给函数模板也赋予类型模板特化的性质。
template <typename T>
typename std::enable_if_t<std::is_integral_v<T>, bool>
is_odd(T t) {
return bool(t%2);
}
用法三:校验函数模板参数类型
有时定义的模板函数,只希望特定的类型可以调用
template <typename T, typename = typename std::enable_if_t<std::is_integral_v<T>>>
bool is_even(T t) {
return t%2 == 0;
}
3. 例程
#include <iostream>
#include <type_traits>
template <typename T>
//typename std::enable_if<std::is_integral<T>::value, bool>::type // c++11的写法
typename std::enable_if_t<std::is_integral_v<T>, bool>
is_odd(T t) {
return bool(t%2);
}
//template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type> // c++11的写法
template <typename T, typename = typename std::enable_if_t<std::is_integral_v<T>>>
bool is_even(T t) {
return !is_odd(t);
}
template <typename T, typename = typename std::enable_if_t<std::is_integral_v<T>>>
bool is_even2(T t) {
return t%2 == 0;
}
template <typename T, typename = int>
T add_1(T t) {
return t+1;
}
template <typename T>
T add_1_v3(T t) {
static_assert(std::is_integral_v<T>, "Type T should be integer");
return t+1;
}
template <typename T>
T add_1_v4(T t) {
return t+1;
}
int main() {
std::cout << is_even(100) << std::endl;
// std::cout << is_even(100.1) << std::endl; // 编译时无法通过std::enable_if_t<std::is_integral_v<T>>
std::cout << is_even2(101) << std::endl;
std::cout << add_1(101) << std::endl;
std::cout << add_1(101.1) << std::endl;
//std::cout << add_1_v3(101.1) << std::endl; // 编译时无法通过static_assert
std::cout << add_1_v3(101) << std::endl;
return 0;
}