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