網(wǎng)站首頁 編程語言 正文
最近整理代碼時(shí)發(fā)現(xiàn)了有人常會使用std::enable_if_t
,據(jù)說這個(gè)是C++14才支持的寫法,因此再次勾起了我的整理欲。但要是熟悉std::enable_if
的話其實(shí)也沒啥太大難度,自認(rèn)為這種使用方式主要提供了一種通過模板偏特化來實(shí)現(xiàn)的類型篩選機(jī)制,某些情況下在設(shè)計(jì)復(fù)雜工程的泛化處理時(shí)能提供一些方便。但能力有限,目前我還沒有發(fā)現(xiàn)哪些非常典型的使用場景能大幅提升性能。
不過整理之前感覺有必要先引入一個(gè)很重要的概念:SFINAE
,這是英文Substitution failure is not an error的縮寫,意思是匹配失敗不是錯(cuò)誤。這句話的意思是:我們使用模板函數(shù)時(shí)編譯器會根據(jù)傳入的參數(shù)來推導(dǎo)適配最合適的模板函數(shù),在某些情況下,推導(dǎo)過程會發(fā)現(xiàn)某一個(gè)或者某幾個(gè)模板函數(shù)推導(dǎo)起來其實(shí)是無法編譯通過的,但只要有一個(gè)可以正確推導(dǎo)并編譯出來,則那些推導(dǎo)得到的可能產(chǎn)生編譯錯(cuò)誤的模板函數(shù)就并不會引發(fā)編譯錯(cuò)誤,即匹配失敗不是錯(cuò)誤。下面舉個(gè)栗子:
struct testA
{
int data;
testA(int val) :data(val){};
};
struct testB :testA
{
typedef double value;
testB(int val) :testA(val){};
};
template<typename T>
typename T::value add(T t1, T t2) // Definition #1
{
return t1.data + t2.data;
}
int add(testA t1, testA t2) // Definition #2
{
return t1.data + t2.data;
}
從代碼編寫角度來說,乍一看總感覺模板函數(shù)存在有問題:在未明確輸入類型是testA
還是testB
時(shí)就貿(mào)然使用了其中的value
類型,如果是其他場景一般會引起編譯器的報(bào)錯(cuò),理論上著實(shí)也不甚嚴(yán)謹(jǐn)。 但依托于模板特化的運(yùn)作機(jī)制,編譯器進(jìn)行類型推導(dǎo)時(shí)會嘗試適配該模板,SFINAE
特性會引導(dǎo)編譯器在遭遇特化失敗后放棄該模板并轉(zhuǎn)向其他函數(shù),且不會報(bào)錯(cuò)。
下面時(shí)執(zhí)行結(jié)果:
int _tmain(int argc, _TCHAR* argv[]){
testA a(1), b(2);
testB c(3), d(4);
add(a, b); // Call #2. Without error (even though there is no testA::value) thanks to SFINAE.
add(c, d); // Call #1
return 0;
}
當(dāng)然,我這個(gè)小例子中使用了一些對于結(jié)構(gòu)體struct的派生繼承,其實(shí)對于C++來說,對于struct的使用也已經(jīng)提升到了新的層面,其中的使用場景我一會再總結(jié)個(gè)小文章吧,點(diǎn)擊前往。
現(xiàn)在開始描述下std::enable_if
的使用方式吧,std::enable_if
顧名思義,滿足條件時(shí)類型有效。作為選擇類型的小工具,其廣泛的應(yīng)用在 C++ 的模板元編程(meta programming)中。基本實(shí)現(xiàn)方式大約為:
template<bool B, class T = void>
struct enable_if {};
template<class T>
struct enable_if<true, T> { typedef T type; };
一個(gè)是普通版本的模板類定義,一個(gè)偏特化版本的模板類定義。
主要在于第一個(gè)參數(shù)是否為true
,當(dāng)?shù)谝粋€(gè)模板參數(shù)為false
的時(shí)候并不會定義type
,只有在第一模板參數(shù)為true
的時(shí)候才會定義type
。
typename std::enable_if<true, int>::type t; //正確,type等同于int
typename std::enable_if<true>::type; //可以通過編譯,沒有實(shí)際用處,推導(dǎo)的模板是偏特化版本,第一模板參數(shù)是true,第二模板參數(shù)是通常版本中定義的默認(rèn)類型即void,但是一般也用不上它。
typename std::enable_if<false>::type; //無法通過編譯,type類型沒有定義
typename std::enable_if<false, int>::type t2; //同上
網(wǎng)上扒過來了一個(gè)用于偏特化的小例子:
template <typename T>
typename std::enable_if<std::is_trivial<T>::value>::type SFINAE_test(T value)
{
std::cout<<"T is trival"<<std::endl;
}
template <typename T>
typename std::enable_if<!std::is_trivial<T>::value>::type SFINAE_test(T value)
{
std::cout<<"T is none trival"<<std::endl;
}
下面是一個(gè)用于校驗(yàn)函數(shù)模板參數(shù)類型的小例子:
template <typename T>
typename std::enable_if<std::is_integral<T>::value, bool>::type
is_odd(T t) {
return bool(t%2);
}
template <typename T, typename = typename std::enable_if<std::is_integral<T>::value>::type>
bool is_even(T t) {
return !is_odd(t);
}
到這里對于enable_if_t
就更通俗易懂了:
template <bool _Test, class _Ty = void>
using enable_if_t = typename enable_if<_Test, _Ty>::type;
可以直接拿來作為類型來使用,以下是我從網(wǎng)上找的一些簡單事例。
1、作為函數(shù)參數(shù)或返回值:
template<typename T>
struct Check1
{
//如果T的類型是int,則定義函數(shù) 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,則定義函數(shù) double read()
template<typename U = T>
typename std::enable_if_t<std::is_same_v<U, double>, U> read() {
return 3.14;
}
}
作為模板參數(shù):
template<typename T>
struct Check2
{
//如果T的類型是int,則定義函數(shù) 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,則定義函數(shù) double read()
template<typename U = T, typename std::enable_if_t<std::is_same_v<U, double>>* = nullptr>
U read() {
return 3.14;
}
};
類型偏特化:
// T是其它類型
template<typename T, typename = void>
struct zoo;
// 如果T是整型(我從網(wǎng)上扒代碼時(shí)原文這里寫的浮點(diǎn),我覺得應(yīng)該是整型吧,
//這里的std::is_integral_v應(yīng)該是C++新標(biāo)準(zhǔn)里的特性,但也類似于std::is_integral<T>::value,據(jù)說在C++17標(biāo)準(zhǔn)之后了。)
template<typename T>
struct zoo<T, std::enable_if_t<std::is_integral_v<T>>>
{
};
最后查閱到據(jù)說C++ 20中通過concepts又做了一些簡化:
#ifdef __cpp_lib_concepts
#include <concepts>
#endif
// 如果T是整數(shù)類型
template<std::integral T>
void display_concepts_1(T num)
{
}
// 如果T是整數(shù)類型
void display_concepts(std::integral auto num)
{
}
// 如果T是浮點(diǎn)數(shù)類型
void display_concepts(std::floating_point auto num)
{
}
等價(jià)于:
// 如果T是整數(shù)類型
template<typename T, typename std::enable_if_t<std::is_integral_v<T>>* = nullptr>
void display_1(T num)
{
}
// 如果T是整數(shù)類型
template<typename T>
void display(typename std::enable_if_t<std::is_integral_v<T>>* = nullptr)
{
}
// 如果T是浮點(diǎn)數(shù)類型
template<typename T>
void display(typename std::enable_if_t<std::is_floating_point_v<T>>* = nullptr)
{
}
就先寫到這里吧,期待下次遇到有意思的問題繼續(xù)記錄。
參考文章:
[1]: https://blog.csdn.net/jeffasd/article/details/84667090
[2]: https://blog.csdn.net/kpengk/article/details/119979733
[3]: https://www.jianshu.com/p/45a2410d4085
原文鏈接:https://blog.csdn.net/wangx_x/article/details/122867422
相關(guān)推薦
- 2022-07-28 Python自動化實(shí)戰(zhàn)之接口請求的實(shí)現(xiàn)_python
- 2022-01-20 關(guān)于 Symbol() 能不能當(dāng)作 key值使用
- 2022-10-07 C語言直接插入排序算法介紹及示例_C 語言
- 2022-03-26 android獲取及監(jiān)聽手機(jī)網(wǎng)絡(luò)狀態(tài)_Android
- 2022-05-20 plotly分割顯示mnist的方法詳解_python
- 2022-07-14 詳解HBase表的數(shù)據(jù)模型_其它綜合
- 2022-06-18 Redis官方可視化工具RedisInsight的安裝使用詳細(xì)教程(功能強(qiáng)大)_Redis
- 2022-12-26 python畫圖時(shí)linestyle,color和loc參數(shù)的設(shè)置方式_python
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細(xì)win安裝深度學(xué)習(xí)環(huán)境2025年最新版(
- Linux 中運(yùn)行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運(yùn)算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認(rèn)證信息的處理
- Spring Security之認(rèn)證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯(cuò)誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實(shí)現(xiàn)加密?
- Spring ioc容器與Bean的生命周期。
- 【探索SpringCloud】服務(wù)發(fā)現(xiàn)-Nac
- Spring Security之基于HttpR
- Redis 底層數(shù)據(jù)結(jié)構(gòu)-簡單動態(tài)字符串(SD
- arthas操作spring被代理目標(biāo)對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊(duì)列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠(yuǎn)程分支