一、 c++ traits
traits是c++模板編程中使用的一種技術,主要功能:
把功能相同而參數不同的函數抽象出來,通過traits將不同的參數的相同屬性提取出來,在函數中利用這些用traits提取的屬性,使得函數對不同的參數表現一致。
traits是一種特性萃取技術,它在Generic Programming中被廣泛運用,常常被用於使不同的類型可以用於相同的操作,或者針對不同類型提供不同的實現.traits在實現過程中往往需要用到以下三種C++的基本特性:
enum、typedef、template (partial) specialization
其中:
enum用於將在不同類型間變化的標示統一成一個,它在C++中常常被用於在類中替代define,你可以稱enum為類中的define;
typedef則用於定義你的模板類支持特性的形式,你的模板類必須以某種形式支持某一特性,否則類型萃取器traits將無法正常工作
template (partial) specialization被用於提供針對特定類型的正確的或更合適的版本.
參考 c++ traits
二、 c++11 中 type_traits
通過type_traits可以實現在編譯期計算、查詢、判斷、轉換和選擇,增強了泛型編程的能力,也增強了程序的彈性,使得我們在編譯期就能做到優化改進甚至排錯,能進一步提高代碼質量。
1. 基本的type_traits
1.1 簡單的type_traits(以定義結構體/類中的常量為例)
定義一個編譯期常量 template<typename T> struct GetLeftSize{ //使用靜態常量 static const int value = 1; //或者使用 enum enum{value = 1}; }; 在c++11中直接繼承 std::integral_constant即可。 template<typename T> struct GetLeftSize : std::integral_constant < int, 1 > { }; int main(){ cout << GetLeftSize<float>::value << endl; return 0; } std::integral_constant的實現: // TEMPLATE CLASS integral_constant template<class _Ty, _Ty _Val> struct integral_constant { // convenient template for integral constant types static const _Ty value = _Val; typedef _Ty value_type; typedef integral_constant<_Ty, _Val> type; operator value_type() const { // return stored value return (value); } };
這樣,在想要在結構體或類中定義一個整型常量,則可以使該結構體或類繼承自 std::integral_constant< int/unsigned int/short/uint8_t/....等整型類型, 想要的值>,則該結構體內部就有了一個 static const的value,訪問該value即可。
std::true_type, std::false_type分別定義了編譯期的true和false類型。
typedef integral_constant<bool, true> true_type; typedef integral_constant<bool, false> false_type;
1.2 類型判斷的type_traits
這些類型判斷的type_traits從std::integral_constant派生,用來檢查模板類型是否為某種類型,通過這些traits可以獲取編譯期檢查的bool值結果。
template<typename T> struct is_integral; //用來檢查T是否為bool、char、char16t_t、char32_t、short、long、long long或者這些類型的無符號整數類型。如果T是這些類型中的某一類型,則std::is_integral::value 為true, 否則為false。 其他的一些類型判斷type_traits template<typename T> struct is_void; //是否為void類型 template<typename T> struct is_floating_point; //是否為浮點類型 is_const, is_function, is_pointer, is_compound.... std::is_const<int>::value //false std::is_const<const int>::value //true
1.3 判斷兩個類型之間關系的traits
traits 類型 | 說明 |
---|---|
template< typename T, typename U> struct is_same; |
判斷兩個類型是否相同 |
template< typename T, typename U> struct is_base_of; |
判斷類型T是否是類型U的基類 |
template< typename T, typename U> struct is_convertible; |
判斷類型T能否轉換為類型U |
和type_traits的其他使用一樣,通過 is_xxx::value 獲得結果(true/false).
1.4 類型的轉換 traits
traits類型 | 說明 |
---|---|
template< typename T> struct remove_const; |
移除const |
template< typename T> struct add_const; |
添加const |
template< typename T> struct remove_reference; |
移除引用 |
template< typename T> struct add_lvalue_reference; |
添加左值引用 |
template< typename T> struct add_rvalue_reference; |
添加右值引用 |
template< typename T> struct remove_extent; |
移除數組頂層的維度, 比如 int [3][3][2] 變為 int [3][2] |
template< typename T> struct remove_all_extent; |
移除數組所有的維度,比如 int [3][3][2] 變為 int |
template< typename T> struct remove_pointer; |
移除指針 |
template< typename T> struct add_pointer; |
添加指針 |
template< typename T> struct decay; |
移除cv或者添加指針 |
template< typename .... T> struct common_type; |
獲取公共類型 |
通過 ::type來訪問這些類型。
std::cout << std::is_same<const int, std::add_const<int>::type>::value << endl; //結果為true std::cout << std::is_same<int, std::remove_all_extent<int[2][2][3]>::type>::value<<endl; //在根據模板參數創建對象時,要注意移除引用: template<typename T> typename std::remove_reference<T>::type* Create(){ typedef typename std::remove_reference<T>::type U; return new U(); } //因為模板參數可能是引用類型,而創建對象時,需要原始的類型,不能用引用類型,所以需要將可能的引用移除 //如果給的模板參數是一個帶cv描述符的引用類型,要獲取它的原始類型,可以使用decay template<typename T> typename std::decay<T>::type* Create(){ typedef typename std::decay<T>::type U; return new U(); } decay還可以獲得函數的指針類型,從而將函數指針變量保存起來,以便在后面延遲調用。 typdef std::decay<int(double)>::type F; //F為一個函數指針類型, int(*)(double) template<typename F> struct SimpleFunction{ using FnTyppe = typename std::decay<F>::type; SimpleFunction(F& f): m_fn(f){}; void Run(){ m_fn(); } FnType m_fn; };
2. 根據條件選擇的traits
std::conditional在編譯期根據一個判斷式選擇兩個類型中的一個,和條件表達式的語義類似,類似於一個三元表達式:
template<bool B, class T, class F> struct conditional; 在std::conditonal模板參數中,如果B為true,則conditional::type為T,否則為F。 std::conditional<true, int, double>::type //= int
3. 獲取可調用對象返回類型的traits
在類型推導的時候,decltype和auto可以實現模板函數的返回類型。比如
//返回類型后置 template<typename F, typename Arg> auto Func(F f, Arg arg)->decltype(f(arg)){ return f(arg); }
c++11提供了另一個traits——result_of,用來在編譯期獲取一個可調用對象的返回類型。
template<typename F, class... ArgTypes> class result_of<F(ArgTypes...)>; int fn(int) {return int();}; typedef int(&fn_ref)(int); typedef int(*fn_ptr)(int); struct fn_class{ int operator()(int i){ return i; } }; int main(){ typedef std::result_of<decltype(fn)&(int)>::type A; //int typedef std::result_of<fn_ref(int)>::type B; //int typedef std::result_of<fn_ptr(int)>::type C; //int typedef std::result_of<fn_class(int)>::type D; //int return 0; } 需要注意 std::result_of<Fn(ArgTypes)> 要去Fn為一個可調用對象,而函數類型不是一個可調用對象,因此,不能使用 typedef std::result_of<decltype(fn)(int)>::type A: //錯誤 需要先將fn轉換為一個可調用對象類型,比如: typedef std::result_of<decltype(fn)&(int)>::type A; typedef std::result_of<decltype(fn)*(int)>::type B; typedef std::result_of<std::decay<decltype(fn)>::type(int)>::type C;
4. 根據條件禁用或啟用某種或某些類型traits
std::enable_if利用SFINAE實現條件選擇重載函數
template< bool B, class T = void> //T為返回類型,常用作函數的返回類型 struct enable_if; ``` `當B為true的時候,返回類型T,否則編譯出錯。` ``` template<class T> //T只有為合法的類型,才能調用該函數,否則編譯出錯 typename std::enable_if<std::is_arithmetic<T>::value, T>::type foo(T t){ return t; } auto r = foo(1); //返回1 auto r1 = foo(1.2); //返回1.2 auto r2 = foo("hello"); //編譯出錯
可以利用這一點來實現相同函數名,但不同類型參數的函數的重載:
//對於arithmetic類型的入參返回0,對於非arithmetic類型返回1,通過arithmetic將所有的入參分為兩大類進行處理。 template<class T> typename std::enable_if<std::is_arithmetic<T>::value, int>::type foo(T t){//函數返回類型為int cout << t << endl; return t; } template<class T> typename std::enable_if<! std::is_arithmetic<T>::value, int>::type foo(T t){//函數返回類型為int cout << typeid(T).name() << endl; return 1; }