1. 程式人生 > >類模板以及其中的traits技術和type classification技術

類模板以及其中的traits技術和type classification技術

1. 類模板相關概念

類模板用來描述一系列具有相同行為的類。一般有如下的形式:

 

template<class T, class U>
class A
{
public:
	A(){ cout<<"Primary template\n";}
private:
    T t;
    U u;
 };


如上所示的類模板我們一般稱之為主類模板

 

例項化後的類模板稱之為特化模板類,當我們給定全部的模板型別時,如下全部給定了T和U的型別時,

 

template<>
class A<int, int>
{
public:
   A() { cout <<"<int, int> Fully specialization\n"; }
private:
   int t;
   int u; 
 };

這稱之為完全特化。當部分指定了類模板的引數型別時,稱之為偏特化,如下所示,

 

 

template<class T>
class A<T, T>
{
public:
    A() { cout <<"<T,T> partial specialization\n";}
private:
    T t;
    T u;
};

當我們由一個類模板建立例項物件時,建立物件採用的模板類的匹配順序為完全特化模板類-->偏特化模板類-->主類模板,示例如下:

 

 

#include <iostream>
using namespace std;

template<class T, class U>
class A
{
public:
    A(){ cout<<"Primary template\n";}
private:
    T t;
    U u;
};

template<>
class A<int, int>
{
public:
  A() { cout <<"<int, int> Fully specialization\n"; }
private:
  int t;
  int u;
};

/*
 *template<>
 *class A<int, float>
 *{
 *public:
 *  A() { cout <<"<int, float> Fully specialization\n"; }
 *};
 */
template<class T, class U>
class A<T*, U>
{
public:
    A() { cout <<"<T*,U> partial specialization\n";}
private:
    T t;
    U u;
};

template<class T>
class A<T, T>
{
public:
    A() { cout <<"<T,T> partial specialization\n";}
private:
    T t;
    T u;
};

template<class U>
class A<int, U>
{
public:
    A() { cout <<"<int,U> partial specialization\n";}
private:
    int t;
    U u;
};

int main()
{
    A<char,int> a1;
    A<char*,int> a2;
    A<float,float> a3;
    A<int, float> a4;
    A<int, int> a5;

    return 0;
}
輸出:
Primary template
<T*,U> partial specialization
<T,T> partial specialization
<int,U> partial specialization
<int, int> Fully specialization

從上面的程式碼我們發現,採用不同的引數例項化出來的類具有不同的特徵,但是他們可以有相同的介面,利用這一點,我們可以將不同資料型別的特徵封裝在一個類模板中,程式其它模組可以使用這個類模板的介面,獲得每個資料型別的特徵資訊。這就是所謂的Traits技術

2. Traits技術

Ttraits在中文中稱之為特性,Traits技術以一個統一的程式設計介面,描述各個資料型別的基本特徵。例如,float和double能夠表示的最小正數一般定義為常量FLT_EPSILON和DBL_EPSILON. 如果我們在一個數值庫中分別要用到float和double, 在需要獲得某個變數的特性時都要檢查該變數的型別,非常麻煩。如果採用Traits技術,我們則可以用統一的介面來獲取這些差異化的特性。

例如:

#include <iostream>
#include <float.h>
using namespace std;

template <typename numT>
struct fp_traits { };

template<>
struct fp_traits<float> {
    typedef float fp_type;
    enum { max_exponent = FLT_MAX_EXP };
    static inline fp_type epsilon()
    {
        return FLT_EPSILON;
    }
};

template<>
struct fp_traits<double> {
    typedef double fp_type;
    enum { max_exponent = DBL_MAX_EXP };
    static inline fp_type epsilon()
    {
        return DBL_EPSILON;
    }
};

template <typename numT>
class matrix {
public:
    typedef numT num_type;
    typedef fp_traits<num_type> num_type_info;
    inline num_type epsilon()
    {
        return num_type_info::epsilon();
    }
    //...
};

int main()
{
    matrix <float>  fm;
    matrix <double> dm;
    cout << "float  matrix: " << fm.epsilon() << endl;
    cout << "double matrix: " << dm.epsilon() << endl;
}

3. type classification技術

根據C++專家Water E.Brown的說法,C++中一般有如下14種類型

 

template <class T> struct is_void;
template <class T> struct is_null_pointer; //<- arrived in C++11 (std::nullptr_t)
template <class T> struct is_integral;
template <class T> struct is_floating_point;
template <class T> struct is_array;
template <class T> struct is_pointer;
template <class T> struct is_lvalue_reference;
template <class T> struct is_rvalue_reference;
template <class T> struct is_member_object_pointer;
template <class T> struct is_member_function_pointer;
template <class T> struct is_enum;
template <class T> struct is_union;
template <class T> struct is_class;
template <class T> struct is_function;

但是C++中和型別相關的運算子僅有sizeof, dynamic_cast, typeid等,他們在獲取C++型別資訊時非常有限,正是因為如此,我們可以基於類模板特化技術,設計專門的類模板來提供所需的資訊,這種方法為型別分類(type classification)技術

 

設想有一個這樣的任務:模板引數T可能是指標型別,引用型別或者陣列(包括vector)中的某一種,我們需要判斷T究竟是哪一個。如果是指標型別,需要知道該指標所指的型別,我們稱為baseT,還需要該型別最底層的型別是哪個C++型別,我們稱為bottomT; 比如對於指標型別int**, baseT為int *;bottomT為int; 對於陣列型別,我們還希望可以獲得其維數

下面的示例程式碼利用type classification技術很好的解決了這一問題:

 

#include <iostream>
#include <vector>
using namespace std;

template<typename T>
class TypeInfo {
public:
    enum { IsPtrT = 0, IsRefT = 0, IsArrayT = 0 };
    enum { Dim = 0 };
    typedef T baseT;
    typedef T bottomT;
};

template<typename T>
class TypeInfo<T*> {
public:
    enum { IsPtrT = 1, IsRefT = 0, IsArrayT = 0 };
    enum { Dim = 0 };
    typedef T baseT;
    typedef typename TypeInfo<T>::bottomT bottomT;
};

template<typename T>
class TypeInfo<T&> {
public:
    enum { IsPtrT = 0, IsRefT = 1, IsArrayT = 0 };
    enum { Dim = 0 };
    typedef T baseT;
    typedef typename TypeInfo<T>::bottomT bottomT;
};

template<typename T, size_t N>
class TypeInfo <T[N]> {
public:
    enum { IsPtrT = 0, IsRefT = 0, IsArrayT = 1 };
    enum { Dim = 1 + TypeInfo<T>::Dim };
    typedef T baseT;
    typedef typename TypeInfo<T>::bottomT bottomT;
};

template<typename T, size_t N>
class TypeInfo <T(*)[N]> {
public:
    enum { IsPtrT = 0, IsRefT = 0, IsArrayT = 1 };
    enum { Dim = 2 + TypeInfo<T>::Dim };
    typedef T baseT;
    typedef typename TypeInfo<T>::bottomT bottomT;
};

template<typename T>
class TypeInfo <std::vector<T> > {
public:
    enum { IsPtrT = 0, IsRefT = 0, IsArrayT = 1 };
    enum { Dim = 1 + TypeInfo<T>::Dim };
    typedef T baseT;
    typedef typename TypeInfo<T>::bottomT bottomT;
};

template<typename T>
size_t dims(T& a)
{
    return TypeInfo<T>::Dim;
}

int main()
{
    TypeInfo<int** >::bottomT  x = 100;
    typedef int* arrayType[100];
    TypeInfo< arrayType >::bottomT  y = 200;
    cout << x << " " << y << endl;


    cout << TypeInfo<int[3]>::Dim << endl;
    cout << TypeInfo<int[3][4]>::Dim << endl;

    int array1[5][6];
    int array2[5][6][7];
    vector<int (*)[6][7]> array3;
    cout << dims(array1) << endl;
    cout << dims(array2) << endl;
    cout << dims(array3) << endl;
    cout << TypeInfo<vector<int[3][4]> >::Dim << endl;
    cout << TypeInfo<vector<vector<int[3][4]> > >::Dim << endl;
}