類模板以及其中的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;
}