c++11——type_traits 類型萃取


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

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM