網(wǎng)站首頁 編程語言 正文
本篇文章旨在引導大家自行實現(xiàn)type_traits的基礎(chǔ)代碼。
模板編程不像常規(guī)的代碼,可以有if-else這些流控制語句,我們需要充分利用模板、模板特例、類型轉(zhuǎn)換等特性來實現(xiàn)編譯期的一系列判斷和類型轉(zhuǎn)換。
定義基礎(chǔ)常量
第一步,我們需要定義true和false兩個常量,所有的type_traits都基于此。我們的目的就是要用一個模板類型來表示是非,其中的value正好是這兩個值。之后我們更高級的判斷類型都是繼承自這兩個類型的其中一個,通過這種方式獲取value值就可以獲取true和false了。
如果聽這個解釋有點暈的話,不要緊,我們直接來看代碼。這里需要注意的是,既然type_traits都是編譯期行為,因此其成員只能是靜態(tài)不可變成員(編譯期就可以確定的成員)。
struct true_type {
static constexpr bool value = true;
};
struct false_type {
static constexpr bool value = false;
};
基礎(chǔ)類型判斷
有了基礎(chǔ)常量,我們可以先做一些簡單的類型判斷,比如說判斷這個類型是不是void。這里的思路是,針對于所有類型的模板,繼承自false_type,而針對于void類型,我們給予一個模板特例,讓他繼承自true_type。這樣一來,只有當類型是void的時候才會推導true,其他的就會推導false。請看例程:
template <typename>
struct is_void : false_type {};
template <>
struct is_void<void> : true_type {};
這里我們可以做一些簡單的測試,來判斷函數(shù)的返回值是否為void:
void test1();
int test2();
int main(int argc, const char *argv[]) {
std::cout << is_void<decltype(test1())>::value << std::endl; // 1
std::cout << is_void<decltype(test2())>::value << std::endl; // 0
return 0;
}
有了判斷void的思路基礎(chǔ),不難寫出判斷其他類型的,比如說判斷是否為浮點數(shù),那么只需要對float,double,long double進行特殊處理即可,請看代碼:
template <typename>
struct is_floating_point : false_type {};
template <>
struct is_floating_point<float> : true_type {};
template <>
struct is_floating_point<double> : true_type {};
template <>
struct is_floating_point<long double> : true_type {};
整型判斷相對復雜一點,需要對char,signed char,unsigned char,short,unsigned short,int,unsigned,long,unsigned long,long long,unsigned long long都進行特例編寫,方法相同,不再贅述。
類型處理
在上一節(jié)編寫is_floating_point的時候可能會發(fā)現(xiàn)這樣的問題:
int main(int argc, const char *argv[]) {
std::cout << is_floating_point<const double>::value << std::endl; // 0
std::cout << is_floating_point<double &>::value << std::endl; // 0
return 0;
}
但是照理來說,const類型以及引用類型不應(yīng)該影響他浮點數(shù)的本質(zhì),當然,我們也可以針對所有的const以及引用情況都編寫模板特例,但這樣太麻煩了,如果有辦法可以去掉const以及引用這些符號,然后再去判斷的話,就會減少我們很多工作量。與此同時,這樣的類型處理在實際編程時也是很有用的。
那么,如何去掉const?請看代碼:
template <typename T>
struct remove_const {
using type = T;
};
template <typename T>
struct remove_const<const T> {
using type = T;
};
同樣的思路,當T是const類型時,我們變換成const T,然后只取出T,其他類型時直接透傳T。
同理,用這種方法也可以去除引用:
template <typename T>
struct remove_reference {
using type = T;
};
template <typename T>
struct remove_reference<T &> {
using type = T;
};
template <typename T>
struct remove_reference<T &&> {
using type = T;
};
因此,is_floating_point就可以改寫成這樣:
// 基礎(chǔ)判斷降級為helper
template <typename>
struct is_floating_point_helper : false_type {};
template <>
struct is_floating_point_helper<float> : true_type {};
template <>
struct is_floating_point_helper<double> : true_type {};
template <>
struct is_floating_point_helper<long double> : true_type {};
// remove_reference和remove_const的聲明
template <typename>
struct remove_const;
template <typename>
struct remove_reference;
// 實際的is_floating_point
template <typename T>
struct is_floating_point : is_floating_point_helper<typename remove_const<typename remove_reference<T>::type>::type> {};
類型選擇
我們搞這樣一系列的類型封裝,最主要的原因是為了在編譯器進行邏輯判斷。因此,必然要進行一個選擇邏輯,也就是當條件成立時,選擇某一個類型,不成立時選擇另一個類型。這個功能非常好實現(xiàn),請看代碼:
template <bool judge, typename T1, typename T2>
struct conditional {
using type = T1;
};
template <typename T1, typename T2>
struct conditional<false, T1, T2> {
using type = T2;
};
當?shù)谝粋€參數(shù)為true時,type就與T1相同,否則就與T2相同。
判斷是否相同
我們有時候還需要判斷兩個類型是否相同,這部分也很好實現(xiàn),請看代碼:
template <typename, typename>
struct is_same : false_type {};
template <typename T>
struct is_same<T, T> : true_type {};
tips
其實按照這些邏輯,我們幾乎可以寫出type_traits中的所有功能了。STL中還實現(xiàn)了合取、析取、取反等操作,只是將邏輯判斷轉(zhuǎn)為了模板形式,這些用起來更方便,但不是必須的。大家感興趣可以閱讀這部分源碼。
實現(xiàn)is_base_of
is_base_of用于判斷兩個類型是否是繼承關(guān)系,在C++中已經(jīng)存在了對應(yīng)的關(guān)鍵字用于判斷:
struct B {};
struct D : B {};
struct A {};
int main(int argc, const char *argv[]) {
std::cout << __is_base_of(B, D) << std::endl; // 1
std::cout << __is_base_of(B, A) << std::endl; // 0
return 0;
}
__is_base_of關(guān)鍵字就可以完成這樣的工作,所以我們封裝它為模板即可:
template <typename B, typename D>
struct is_base_of : conditional<__is_base_of(B, D), true_type, false_type> {};
但除了這種直接使用編譯器提供的關(guān)鍵字外,這個功能還有一種其他的實現(xiàn)方法。
如何判斷一個類是否為一個類的父類呢?其實就看指針能否轉(zhuǎn)換(多態(tài))即可。請看代碼:
template <typename B, typename D>
true_type test_is_base(B *);
template <typename B, typename D>
false_type test_is_base(void *);
template <typename B, typename D>
struct is_base_of : decltype(test_is_base<B, D>(static_cast<D *>(nullptr))) {};
如果D是B的子類,那么就會調(diào)用第一個函數(shù),從而推斷出返回值是true_type,否則調(diào)用第二個函數(shù),推斷出返回值是false_type。
不過這樣做還必須加一個判斷,就是B和D必須都是類才行,而且需要去掉const等因素,詳細代碼讀者可以自行嘗試,不再贅述。
原文鏈接:https://blog.csdn.net/fl2011sx/article/details/119353653
相關(guān)推薦
- 2022-09-10 Nginx代理Partainer的實現(xiàn)_nginx
- 2022-03-24 .NET?6開發(fā)TodoList應(yīng)用之實現(xiàn)API版本控制_實用技巧
- 2022-04-30 C#實現(xiàn)鼠標消息捕獲_C#教程
- 2022-05-06 golang excel數(shù)據(jù)寫入到sqlite3中
- 2022-03-11 CentOS?8安裝Docker的詳細教程_docker
- 2023-07-18 Springboot獲取jar包同級目錄
- 2022-09-23 Matlab幾個常用的繪圖顏色搭配推薦_相關(guān)技巧
- 2022-05-15 Element框架里日期選擇器限制時間,最多選31天
- 最近更新
-
- window11 系統(tǒng)安裝 yarn
- 超詳細win安裝深度學習環(huán)境2025年最新版(
- Linux 中運行的top命令 怎么退出?
- MySQL 中decimal 的用法? 存儲小
- get 、set 、toString 方法的使
- @Resource和 @Autowired注解
- Java基礎(chǔ)操作-- 運算符,流程控制 Flo
- 1. Int 和Integer 的區(qū)別,Jav
- spring @retryable不生效的一種
- Spring Security之認證信息的處理
- Spring Security之認證過濾器
- Spring Security概述快速入門
- Spring Security之配置體系
- 【SpringBoot】SpringCache
- Spring Security之基于方法配置權(quán)
- redisson分布式鎖中waittime的設(shè)
- maven:解決release錯誤:Artif
- restTemplate使用總結(jié)
- Spring Security之安全異常處理
- MybatisPlus優(yōu)雅實現(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被代理目標對象命令
- Spring中的單例模式應(yīng)用詳解
- 聊聊消息隊列,發(fā)送消息的4種方式
- bootspring第三方資源配置管理
- GIT同步修改后的遠程分支