C++類型萃取之type_traits和type_info

  • 類型萃取
    • 類型判斷
    • typeid
    • decltype和declval
    • enable_if

類型萃取

通過type_traits可以實現在編譯期計算、查詢、判斷、轉換和選擇,增強了泛型編程的能力,也增強了我們程序的彈性,讓我們能夠在編譯期就能夠優化改進甚至排錯,進一步提高代碼質量。

頭文件 #include

類型判斷

type_trits提供了豐富的編譯期計算、查詢、判斷、轉換和選擇的幫助類,在很多場合中會使用到這些特性。

type_trits的類型選擇功能,在一定程度上可以消除冗長的switch-case或者if-else的語句,降低程序的復雜程度。

這些類型判斷的方法從std::integral_constant派生,用來檢查模板類型是否為某種類型,通過這些trait可以獲取編譯期檢查的bool值結果。

下面的表格是一些常用的判斷類型traits。更過從網址 點擊獲取。

traits類型說明
template
struct is_void;
T是否為void類型
template
struct is_floating_point;
T是否為浮點類型
template
struct is_array;
T是否為數組類型
template
struct is_pointer;
T是否為指針類型(包括函數指針,但不包括成員(函數)指針)
template
struct is_enum;
T是否為枚舉類型
template
struct is_union;
T是否為非union的class/struct類型
template
struct is_class;
T是否為類類型而不是union類型
template
struct is_funtion;
T是否為函數類型
template
struct is_reference;
T是否為引用類型(左值引用或者右值引用)
template
struct is_arithmetic;
T是否為整型和浮點類型
template
struct is_fundamental;
T是否為整型、浮點、void、或nullptr_t類型
template
struct is_object;
T是否為一個對象類型(不是函數、不是引用、不是void)
template
struct is_scalar;
T是否為arithmetic、enumeration、pointer、pointer to member或std::nullptr_t類型
template
struct is_compound;
T是否非fundamental類型構造的
template
struct is_member_pointer;
T是否為成員函數指針類型
template
struct is_polymorphic;
T是否有虛函數
template
struct is_abstract;
T是否為抽象類
template
struct is_signed;
T是否是有符號類型
template
struct is_unsigned;
T是否是無符號類型
template
struct is_const;
T是否為const修飾的類型

使用方法:

#include <iostream>
#include <type_traits>int main()
{std::cout << "is_const:" << std::endl;std::cout << "int: " << std::is_const<int>::value << std::endl;std::cout << "const int: " << std::is_const<const int>::value << std::endl;return 0;
}輸出結果為:  is_const:
int: 0
const int: 1

判斷類型的traits一般和std::enable_if結合起來使用,通過SFINAE特性來實現功能更強大的重載。后面會講到。

判斷兩個類型之間的關系traits

traits說明
template
struct is_same;
判斷兩個類型是否相同
template
struct is_base_of;
判斷Base類型是否為Derived類型的基類
template
struct is_convertible;
判斷前面的模板參數類型能否轉換為后面的模板參數類型

簡單介紹一下is_same的用法:

#include <iostream>
#include <type_traits>int main()
{std::cout << "int: " << std::is_same<int, int>::value << std::endl;//這里使用了decltype可以獲取變量的類型為intstd::cout << "int: " << std::is_same<decltype(a), int>::value << std::endl;std::cout << "const int: " << std::is_same<int, unsigned int>::value << std::endl;return 0;
}輸出結果為:
int: 1
int: 1
const int: 0

類型的轉換traits

常用的類型轉換traits包括對const的修改—-const的移除和添加,引用的修改—–引用的移除和添加,數組的修改和指針的修改。

下表為類型轉換的方法:

traits說明
template
struct remove_const;
移除const
template
struct add_const;
添加const
template
struct remove_reference;
移除引用
template
struct add_lvalue_reference;
添加左值引用
template
struct add_rvalue_reference;
添加右值引用
template
struct remove_extents;
移除數組頂層的維度
template
struct remove_all_extents;
移除數組所有的維度
template
struct remove_pointer;
移除指針
template
struct add_pointer;
添加指針
template
struct decay;
移除cv或添加指針
template
struct common_type;
獲取公共類型

簡單介紹一下使用方法:

具體可以參考c++11深入理解93頁。

#include <iostream>
#include <type_traits>int main()
{std::cout << "int: " << std::is_same<int, add_const<int>>::value << std::endl;return 0;
}輸出結果為:
int: 0

typeid

包含頭文件 #include

在講解typeid神秘面紗之前,我們先了解一下,RTTI(Run-Time Type Identification),中文為運行時類型識別,它使程序能夠獲取由基指針或引用所指向的對象的實際派生類型。即允許 “用指向基類的指針或引用來操作對象” 的程序能夠獲取到 “這些指針或引用所指對象” 的實際派生類型。

在C++中,為了支持RTTI提供了兩個操作符:dynamic_cast和typeid。

  • dynamic_cast允許運行時刻進行類型轉換,從而使程序能夠在一個類層次結構中安全地轉化類型,與之相對應的還有一個非安全的轉換操作符static_cast,因為這不是本文的討論重點,所以這里不再詳述,感興趣的可以自行查閱資料。
  • typeid是C++的關鍵字之一,等同于sizeof這類的操作符。typeid操作符的返回結果是名為type_info的標準庫類型的對象的引用。

我們來看一下如何使用:

#include <typeinfo>struct Base { virtual ~Base() = default; };
struct Derived : Base {};int main()
{Base b1;Derived d1;const Base *pb = &b1;std::cout << typeid(*pb).name() << '\n';pb = &d1;std::cout << typeid(*pb).name() << '\n';std::cout << typeid(1).name() << '\n';std::cout << typeid(2.444).name() << '\n';return 0;
}輸出結果:
4Base
7Derived
i
d

上面是在gcc編譯上編譯的,結果與vc++,clang都大不相同。

decltype和declval

有時候要獲取函數的返回類型是一件比較困難的事情:
比如下面代碼:

template <typename F, typename Arg>
?? func(F f, Arg arg)
{return f * arg;
}

由于函數的入參都是兩個模板參數,導致我們不能直接確定返回類型,那么我們可以通過decltype來推斷函數返回類型。

template <typename F, typename Arg>
decltype((*(F*)0)*((*(Arg*)0))) func(F f, Arg arg)
{return f * arg;
}

上面的比較繁瑣,所以我們可以使用返回類型后置去簡化。

template <typename F, typename Arg>
auto func(F f, Arg arg)->decltype(f * arg )
{return f * arg;
}

這樣看起來就舒服多了。

但是有些時候我們不能通過decltype來獲取類型了,如下面:

#include <type_traits>class A
{A()=delete;
public:int operator() ( int i ){return i;}
};int main()
{int a = A()(3);decltype( A()(0) ) i = 4;std::cout << i << std::endl;std::cout << a << std::endl; //輸出結果為3return 0;
}

上面的代碼將會編譯報錯,因為A沒有默認構造函數,對于這種沒有默認構造函數的類型,我們如果希望能推導其成員函數的返回類型,則需要借助std::declval。

修改為:

decltype( std::declval<A>()(std::declval<int>())) i = 4;

上面的代碼可以通過,因為std::declval能夠獲取任何類型的臨時值,不管它有沒有默認構造函數。因為我們通過declval()獲取了A的臨時對象。需要注意一點,declval獲取的臨時值不能用于求值,因此必須使用decltype來推斷出最終的返回類型。

其實上面做了這么多,還是比較麻煩,C++11提供了另外一個trait——std::result_of,用來在編譯期獲取一個可調用對象的返回類型。

上面的代碼改寫如下:

std::result_of<A(int)>::type i = 4;

這段代碼實際上等價于 decltype( std::declval()(std::declval()))。

enable_if

在講enable_if之前我們先來了解什么是SFINAE,它是Substitution failure is not an error 的首字母縮寫。

我們通過一個例子來了解一下SFINAE機制:

template<typename T>
void Fun(T *t)
{*t *+= 1;
}template<typename T>
void Fun(T t)
{t += 1;
}int main()
{Fun(1);return 0;
}

上面運行的時候,將會匹配到第二個重載函數,在匹配的過程中,當匹配到void Fun(T t)時,將一個非0的整數來替換T 是錯誤的,此時編譯器并不會報錯,此時就叫failure,然后繼續匹配其他的重載函數,如果最后發現void Fun(T t)能匹配上,整個過程就不會報錯,如果匹配不到就會報error,這就是為什么叫Substitution failure is not an error。

這個規則就叫SFINAE

std::enable_if利用SFINAE實現根據條件選擇重載函數,std::enable_if的原型如下:

template<bool B, class T = void>
struct enable_if;

簡單介紹一下使用的方法

//is_arithmetic為判斷是否為整型和浮點類型的traits
//這里在使用的時候需要加上typename在enable_if前面
//是要告訴編譯器后面的標識符是一個類型名來處理,否則會被編譯器當做靜態變量處理
template<class T>
typename std::enable_if<std::is_arithmetic<T>::value, T>::type foo(T t)
{return t;
}int main()
{auto r = foo(1);auto r1 = foo(1.2);std::cout << r <<std::endl; //1,整數std::cout << r1 <<std::endl; //1.2,浮點數//auto r2 = foo("test"); //編譯錯誤return 0;
}

上面的函數模板通過enable_if做了限定,只能接受整型和浮點型,我們來看一下foo(1)運行步驟:

1. 根據傳入的實參1,推斷出T為int類型
2. std::is_arithmetic<T>::value變為std::is_arithmetic<int>::value,此時返回值為true
3. std::enable_if<std::is_arithmetic<T>::value, T>中的最后一個T為int,通過::type獲取類型。
4. foo函數的返回值被確定為int類型  

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/536147.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/536147.shtml
英文地址,請注明出處:http://en.pswp.cn/news/536147.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

使用Phpstorm實現遠程開發

Phpstorm除了能直接打開本地文件之外&#xff0c;還可以連接FTP&#xff0c;除了完成正常的數據傳遞任務之外&#xff0c;還可以進行本地文件與服務端文件的異同比較&#xff0c;同一文件自動匹配目錄上傳&#xff0c;下載&#xff0c;這些功能是平常IDE&#xff0c;FTP軟件中少…

什么是遞歸函數?

文章目錄遞歸函數遞歸例題特點效率優點遞歸函數 遞歸 遞歸就是一個函數在它的函數體內調用它自身。執行遞歸函數將反復調用其自身&#xff0c;每調用一次就進入新的一層。遞歸函數必須有結束條件。 當函數在一直遞推&#xff0c;直到遇到墻后返回&#xff0c;這個墻就是結束條…

apache ab壓力測試報錯

今天用apache 自帶的ab工具測試&#xff0c;當并發量達到1000多的時候報錯如下&#xff1a; [rootaa~]# This is ApacheBench, Version 2.3 <Revision:655654> Copyright 1996 Adam Twiss, Zeus Technology Ltd, http://www.zeustech.net/ Licensed to The Apache Sof…

ngOnInit與constructor的區別

前世今生 Angular會管理一個組件的生命周期,包括組件的創建、渲染、子組件創建與渲染、當數據綁定屬性變化時的校驗、DOM移除之前毀銷。 Angular提供組件生命周期鉤子便于我們在某些關鍵點發生時添加操作。 組件生命周期鉤子 指令和組件實例有個生命周期用于創建、更新和銷…

Nginx配置性能優化

大多數的Nginx安裝指南告訴你如下基礎知識——通過apt-get安裝&#xff0c;修改這里或那里的幾行配置&#xff0c;好了&#xff0c;你已經有了一個Web服務器了。而且&#xff0c;在大多數情況下&#xff0c;一個常規安裝的nginx對你的網站來說已經能很好地工作了。然而&#xf…

Angular的@Output與@Input理解

@Output與@Input理解 Output和Input是兩個裝飾器,是Angular2專門用來實現跨組件通訊,雙向綁定等操作所用的。 @Input Component本身是一種支持 nest 的結構,Child和Parent之間,如果Parent需要把數據傳輸給child并在child自己的頁面中顯示,則需要在Child的對應 directiv…

騰訊云CDN配置

第一步&#xff1a;先去領取騰訊云CDN免費包23333333 以下為正式步驟&#xff1a; 在這里體現大家&#xff0c;域名一定要備案&#xff0c;另外要明白域名如何解析 前邊問題不大&#xff0c;一切跟著騰訊云的套路來即可&#xff0c;需要注意的是網上后優化的配置大家可以自行…

Promise.all的深入理解

異步之Promise Promise.all Promise.all接收的promise數組是按順序執行的還是一起執行的&#xff0c;也就是說返回的結果是順序固定的嗎&#xff1f; 目前有兩種答案&#xff1a; 應該是同步執行的&#xff0c;但是這樣就有效率問題了&#xff0c;如果想改成異步執行怎么辦…

wordpress后臺無法登錄問題

之前給自己的WordPress加了個標簽云&#xff0c;今天登錄的時候突然發現網站后臺進不去了&#xff0c;無奈各種找材料&#xff0c;這算是皇天不負有心人&#xff0c;總算是給我找到了&#xff0c;現在做一下記錄 登錄不上的原因在于&#xff1a;wp-admin和wp-admin/是不同的&a…

深入理解Angular訂閱者模式

深入理解Angular訂閱者模式 如果正在讀此篇文章的你學過java,c++等面向對象語言,知道兩個模式觀察者模式和訂閱者模式,分別為:Observer pattern,Pub-sub pattern(Subscriber) 接下來我們結合Angular來說明這兩個模式。 Observer pattern This is a pattern of developme…

Ubuntu中安裝python3

通過命令行安裝Python3.*&#xff0c;只需要在終端中通過命令行安裝即可&#xff1a; sudo apt-get install python3 Ubuntu的底層大多數采用的是Python2.*&#xff0c;Python3和Python2是互相不兼容的&#xff0c;完全沒法通用的&#xff08;也不知道他們怎么想的o(TヘTo)&a…

Angular深入理解之指令

Angular深入理解之指令 指令有什么功能 Attribute directives 屬性指令Structural directives 結構指令自定義屬性指令自定義結構指令Angular深入理解之指令 對于初學Angular的同學來說,指令無疑是最痛苦的,那么我們怎么使用自定義的指令呢?指令到底怎么實現呢?為什么要寫…

windows下Apache虛擬主機配置

找到host文件&#xff1a;C:\Windows\System32\drivers\etc\hosts 在hosts這么增加&#xff1a; 127.0.0.1 666.666.com 127.0.0.1 777.777.com 修改httpd.conf文件&#xff1a; 打開文件&#xff1a;xxx\xampp\apache\conf\httpd.conf 找到#LoadModule vhost_…

Angular深入理解基本組成

Angular深入理解基本組成 在講指令時,我們先來了解一下Angular的基本概念和結構。 Module 模塊 Angular 是模塊化的.Modules 導出 classes, function, values , 以便在其他模塊導入使用.angular應用由模塊組成,每個模塊都做此模塊相關的事情組件、方法、類、服務等,他們都…

1607: 字符棱形

1607: 字符棱形 根據讀入的字符和邊長&#xff0c;勾畫字符棱形。 Input 輸入數據含有不超過50組的數據&#xff0c;每組數據包括一個可見字符c和一個整數n&#xff08;1≤n≤30&#xff09;。 Output 輸出以c為填充字符&#xff0c;邊長為n的棱形&#xff0c;勾畫每個棱形…

Angular深入理解管道Pipe

Angular深入理解管道 純管道與非純管道區別的本質 Pure FunctionImpure Function內置Pipe pipe使用自定義Pipe 管道性能優化Angular深入理解管道 管道的鏈接 有學過linux shell的同學,應該知道管道,在shell中的管道是IPC,linux的進程間通訊有pipe,FIFO,signal。這里只是簡單…

1959: 圖案打印

1959: 圖案打印 Description 一年一度的植樹節就要到了&#xff0c;計算機學院學生準備在學院教學樓門前的空地上種植樹木。為使樹木排列得更加美觀&#xff0c;大家決定把樹木排列成菱形。現在告訴你我們所擁有的樹木能排列成邊長為N的菱形&#xff0c;請你編程輸出樹木所排…

JS事件的捕獲和冒泡階段

JS事件的捕獲和冒泡階段 這里介紹兩個事件模型&#xff1a;IE事件模型與DOM事件模型 IE內核瀏覽器的事件模型是冒泡型事件&#xff08;沒有捕獲事件過程&#xff09;&#xff0c;事件句柄的觸發順序是從ChildNode到ParentNode。 <div id"ancestor"> <butt…

2016: C語言實驗——打印金字塔

2016: C語言實驗——打印金字塔 Description 輸入n值&#xff0c;打印下列形狀的金字塔&#xff0c;其中n代表金字塔的層數。 Input 輸入只有一個正整數n。 Output 打印金字塔圖形&#xff0c;其中每個數字之間有一個空格。 Sample Input 3 Sample Output 11 2 1 1 2 …

Anuglar中正確導入RxJS庫

Anuglar中正確導入RxJS庫 目前Angular2中的已經內建支持RxJS,所以我們在使用的時候可以直接導入使用了。 理解操作符導? 在使用創建依賴于 RxJS 組件,服務,指令等等時, 你可能遇到處理運算符導?的問 題。 在項?中引?操作符最主要的?式像下?這樣導?: import rxj…