<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
本篇文章旨在引導大家自行實現type_traits的基礎程式碼。
模板程式設計不像常規的程式碼,可以有if-else這些流控制語句,我們需要充分利用模板、模板特例、型別轉換等特性來實現編譯期的一系列判斷和型別轉換。
第一步,我們需要定義true和false兩個常數,所有的type_traits都基於此。我們的目的就是要用一個模板型別來表示是非,其中的value正好是這兩個值。之後我們更高階的判斷型別都是繼承自這兩個型別的其中一個,通過這種方式獲取value值就可以獲取true和false了。
如果聽這個解釋有點暈的話,不要緊,我們直接來看程式碼。這裡需要注意的是,既然type_traits都是編譯期行為,因此其成員只能是靜態不可變成員(編譯期就可以確定的成員)。
struct true_type { static constexpr bool value = true; }; struct false_type { static constexpr bool value = false; };
有了基礎常數,我們可以先做一些簡單的型別判斷,比如說判斷這個型別是不是void。這裡的思路是,針對於所有型別的模板,繼承自false_type,而針對於void型別,我們給予一個模板特例,讓他繼承自true_type。這樣一來,只有當型別是void的時候才會推導true,其他的就會推導false。請看例程:
template <typename> struct is_void : false_type {}; template <> struct is_void<void> : true_type {};
這裡我們可以做一些簡單的測試,來判斷函數的返回值是否為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的思路基礎,不難寫出判斷其他型別的,比如說判斷是否為浮點數,那麼只需要對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都進行特例編寫,方法相同,不再贅述。
在上一節編寫is_floating_point的時候可能會發現這樣的問題:
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型別以及參照型別不應該影響他浮點數的本質,當然,我們也可以針對所有的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就可以改寫成這樣:
// 基礎判斷降級為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> {};
我們搞這樣一系列的型別封裝,最主要的原因是為了在編譯器進行邏輯判斷。因此,必然要進行一個選擇邏輯,也就是當條件成立時,選擇某一個型別,不成立時選擇另一個型別。這個功能非常好實現,請看程式碼:
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; };
當第一個引數為true時,type就與T1相同,否則就與T2相同。
我們有時候還需要判斷兩個型別是否相同,這部分也很好實現,請看程式碼:
template <typename, typename> struct is_same : false_type {}; template <typename T> struct is_same<T, T> : true_type {};
其實按照這些邏輯,我們幾乎可以寫出type_traits中的所有功能了。STL中還實現了合取、析取、取反等操作,只是將邏輯判斷轉為了模板形式,這些用起來更方便,但不是必須的。大家感興趣可以閱讀這部分原始碼。
is_base_of用於判斷兩個型別是否是繼承關係,在C++中已經存在了對應的關鍵字用於判斷:
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關鍵字就可以完成這樣的工作,所以我們封裝它為模板即可:
template <typename B, typename D> struct is_base_of : conditional<__is_base_of(B, D), true_type, false_type> {};
但除了這種直接使用編譯器提供的關鍵字外,這個功能還有一種其他的實現方法。
如何判斷一個類是否為一個類的父類別呢?其實就看指標能否轉換(多型)即可。請看程式碼:
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的子類,那麼就會呼叫第一個函數,從而推斷出返回值是true_type,否則呼叫第二個函數,推斷出返回值是false_type。
不過這樣做還必須加一個判斷,就是B和D必須都是類才行,而且需要去掉const等因素,詳細程式碼讀者可以自行嘗試,不再贅述。
到此這篇關於C++超詳細分析type_traits的文章就介紹到這了,更多相關C++ type_traits內容請搜尋it145.com以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援it145.com!
相關文章
<em>Mac</em>Book项目 2009年学校开始实施<em>Mac</em>Book项目,所有师生配备一本<em>Mac</em>Book,并同步更新了校园无线网络。学校每周进行电脑技术更新,每月发送技术支持资料,极大改变了教学及学习方式。因此2011
2021-06-01 09:32:01
综合看Anker超能充系列的性价比很高,并且与不仅和iPhone12/苹果<em>Mac</em>Book很配,而且适合多设备充电需求的日常使用或差旅场景,不管是安卓还是Switch同样也能用得上它,希望这次分享能给准备购入充电器的小伙伴们有所
2021-06-01 09:31:42
除了L4WUDU与吴亦凡已经多次共事,成为了明面上的厂牌成员,吴亦凡还曾带领20XXCLUB全队参加2020年的一场音乐节,这也是20XXCLUB首次全员合照,王嗣尧Turbo、陈彦希Regi、<em>Mac</em> Ova Seas、林渝植等人全部出场。然而让
2021-06-01 09:31:34
目前应用IPFS的机构:1 谷歌<em>浏览器</em>支持IPFS分布式协议 2 万维网 (历史档案博物馆)数据库 3 火狐<em>浏览器</em>支持 IPFS分布式协议 4 EOS 等数字货币数据存储 5 美国国会图书馆,历史资料永久保存在 IPFS 6 加
2021-06-01 09:31:24
开拓者的车机是兼容苹果和<em>安卓</em>,虽然我不怎么用,但确实兼顾了我家人的很多需求:副驾的门板还配有解锁开关,有的时候老婆开车,下车的时候偶尔会忘记解锁,我在副驾驶可以自己开门:第二排设计很好,不仅配置了一个很大的
2021-06-01 09:30:48
不仅是<em>安卓</em>手机,苹果手机的降价力度也是前所未有了,iPhone12也“跳水价”了,发布价是6799元,如今已经跌至5308元,降价幅度超过1400元,最新定价确认了。iPhone12是苹果首款5G手机,同时也是全球首款5nm芯片的智能机,它
2021-06-01 09:30:45