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;
}
原文地址:https://www.cnblogs.com/gnivor/p/15546247.html