一、 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;
}
