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;
}


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM