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