??C++类中不仅可以定义数据成员和函数成员,而且还可以定义数据类型成员。在泛型设计中,类的数据类型成员是一个常用的感念。所谓类的数据类型成员,就是在一个类中使用typedef定义一个已知数据类型的别名。例如:
typedef long double LDBL
??在C++中,这种在类模板中定义的数据类型也称nested type(嵌入式类型)。既然nested type与字段、方法都属于类成员,那么当然可以为它们赋予响应的访问属性。于是,这些具有public属性的类型成员就成为类外部模块可以使用的公有类型成员。显然,外部模块就可以通过这些公有类型成员,实现与类模板之间的功能协作。
template<typename T1, typename T2>
class MyTraits{
public:
typedef T1 my_Type1;
typedef T2 my_Type2;
};
int main(int argc, char** argv){
//类外引用类模板的公有类型成员,与引用静态成员一样。
//为了区别,需要加上typename关键字
typename MyTraits<int, double>::my_Type1 t1;
typename MyTraits<int, double>::my_Type2 t2;
}
??typedef字面的意思是类型定义,或定义类型。但是它不能凭空定义一个类型,它只能为某个现有类型(系统固有的、用户自定义的)命名一个别名。人们常常使用别名的原因相同,那就是在某些时候使用别名会带来极大的好处。
typedef int Phone;
typedef int Age;
//很明显wangxin_P这个变量就是用来存储电话的!
Phone Wangxin_P;
/*
一个数组,数组存放的是:
函数指针:{
参数:指针函数:{
参数:空;
返回值:空;
}
返回值:空;
}
*/
void ( *b[10] )( void(*)() );
//pFunParam是一个函数指针,指向参数为空,返回值为空的函数类型。
typedef void(* pFunParam) ();
//pFunx是一个函数指针,指向参数为别名为pFunParam的类型,返回值为空。
typedef void(*pFunx) (pFunParam);
void (*b[10])(void(*)());
pFunx b[10];
/*
以上两者等价:
声明一个数组b,具有十个元素,元素类型是:
函数指针:{
参数:函数指针:{
参数:空;
返回值:空;
}
返回值:空;
}
*/
//平台A
typedef int UINT32;
//平台B
typedef unsigned int UINT32;
??为方便使用,一个类模板会把用 typedef 定义的所有公有数据类型集中形成一个数据类型表,并放在模板中靠前的位置。这个数据类型表存在于类模板内部,所以叫内嵌式数据类型表。
??为了充分利用从模板参数中获得数目有限的数据类型,一个模板通常会尽可能利用那些从模板参数获得的类型资源,除了把通过模板参数传递过来的类型定义成公有成员之外,也把它们的衍生类型也一并定义成公有成员。
//有人把这种数据类型表形象地称为类型榨取机,或者类型萃取机
template<typename T>
struct map{
typedef T value_type;
typedef T& reference;
typedef T* pointer;
}
??在一个类模板中,如果其全部成员都是公有数据类型,那么相对于嵌入式数据类型表,这个类模板就叫独立数据类型表,简称数据类型表。
??这种表通常被用于规范模板类型表。假如有一个项目,要求项目中的模板都必须至少使用两个参数,而且必须向外提供这两个参数的值类型及引用类型,那么为了说明这个要求,项目负责人就可以写一个如下独立数据类型表。
template<typename T, typename U>
class TypeTb1{
public:
typedef T value_type1;
typedef T& reference1;
typedef U value_type2;
typedef U& reference2;
};
??然后要求其他人所设计的类模板必须继承自这个独立类型表。于是凡是继承了 TypeTb1 的类就都有一张与 TypeTb1 完全相同的类型表。简单地说,这种独立类型表就是一种接口,是一种类型接口。
template<typename T, typename U>
class A:public TypeTb1<T, U>
{
//类A的设计内容
};
//例如STL使用binary类模板描述了它对标准二元函数(有两个形参的函数)的要求
template<class _A1, class _A2, class _R>
struct binary
{
typedef _A1 Arg1;
typedef _A2 Arg2;
typedef _R Rtn;
}
template<typename T>
class Num:public TypeTb1<T>
{
//继承了TypeTb1
};
//普通测试函数,可以发布Num<int>的数据类型表
//缺点,重用性差,每次都需要修改模板参数
//事实证明编译器已经只能到可以省略显式调用数据类型表时的typename关键字了
void Tfunction(Num<int>::value_type x,
Num<int>::reference y,
Num<int>::pointer z){
cout<<"x="<<x<<endl;
cout<<"y=&x="<<y<<endl;
cout<<"*z="<<z<<endl;
}
//测试函数模板,注意:
//1.Num不是类型,Num<int>才是类型。
//2.需要使用关键字typename。隐式调用需要关键字。
template<typename T>
void Tfunc(typename T::value_type x,
typename T::reference y,
typename T::pointer z){
cout<<"x="<<x<<endl;
cout<<"y=&x="<<y<<endl;
cout<<"*z="<<z<<endl;
}
int main(int argc, char** argv){
//显式调用时,typename关键字可以省略
typename Num<int>::value_type a = 100;
//调用普通测试函数
Tfunction(a, a, &a);
//调用测试函数模板,类型为Num<int>,而不是Num,Num类模板。
Tfunc<Num<int>>(a, a, &a);
return 0;
}
??众所周知,数据类型表就是一种类模板,而由特化类模板形成的数据类型表就是特化类型表。特化类型表的作用之一就是为了实现同一业务逻辑不同接口的统一。
class Test1{
public:
char compute(int x, double y){
return x;
}
};
class Test2{
public:
int compute(double x, double y){
return x;
}
};
//Test1和Test2两个类的逻辑相同,只是接口不同
//出于代码重用的方式,我们可以写成一个类模板
template<typename Arg1, typename Arg2, typename Ret>
class Test{
public:
Ret compute(Arg1 x, Arg2 y){
return x;
}
};
//这种方法的缺点就是我们需要在声明的时候给出三个模板参数
Test<int, double, char> t1;
//但是如果只是区分两个类,不考虑其他参数类型情况,我们不必如此麻烦
//我们可以使用一套统一的数据类型表,作为类的接口
template<typename T>
class TypeTb1{
typedef int ret_type; //返回值数据类型
typedef int par1_type; //参数1的数据类型
typedef int par2_type; //参数2的数据类型
};
//声明两个空类,作为两个标识符,注意Test1和Test2都是类型
class Test1;
class Test2;
//特例化数据类型表,当模板参数为Test1类时
template<>
class TypeTb1<Test1>{
typedef char ret_type; //返回值数据类型
typedef int par1_type; //参数1的数据类型
typedef double par2_type; //参数2的数据类型
};
template<>
class TypeTb1<Test2>{
typedef int ret_type; //返回值数据类型
typedef double par1_type; //参数1的数据类型
typedef double par2_type; //参数2的数据类型
};
//使用统一数据类型表作为接口,声明的类模板
template<typename T>
class Test{
public:
//当模板参数为Test1或者Test2时,生成特例化类型表
//所以compute函数的参数与返回值类型跟着改变
typename TypeTb1<T>::ret_type compute(
typename TypeTb1<T>::par1_type x,
typename TypeTb1<T>::par2_type y){
return x;
}
}
//成功通过一个模板参数区分了两个类
Test<Test1> t1;
Test<Test2> t2;
??Traits 实际上是特化数据类型表在STL中的一个具体应用,也是数据类型表。但是因为他构思巧妙,备受推崇,人称Traits技巧。
template<typename T>
class Iterator_1{
public:
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
//其余代码···
}
//Iterator_2, Iterator_3 ......
//基础模板
template<typename T>
struct Traits{
//空表
}
//指针的特化模板
template<typename T>
struct Traits<T*>{
typedef T value_type;
typedef value_type* pointer;
typedef value_type& reference;
}
//Traits表特别体现了数据类型表的“类型压榨”能力
//即它不仅能把用户经由模板参数传递来的一个数据类型的所有衍生类型都能提取出来,
//而且还可以把衍生类型的原生数据类型压榨出来。
Traits<double*>::value_type t1 = 100;
cout<<t1<<endl;
//这里的模板参数T的实参将来都是各个迭代器类以及T*。
template<typename T>
struct Traits{
//因为编译器不认识带有域分隔符::的类型,因此这里需要使用关键字typename
typedef typename T::value_type value_type; //隐式调用数据类型成员
typedef typename T::pointer pointer;
typedef typename T::reference reference;
}
//指针特化模板与之前一样。
//测试如下,由此可见数据类型表都变成了Traits,统一了接口
Traits<Iterator_1<int>>::value_type t1 = 100;
Traits<Iterator_2<double>>::value_type t2 = 9.23;
Traits<double*>::value_type t3 = 3.33;第二章:C++泛型机制的基石:数据类型表——《C++泛型:STL原理和应用》读书笔记整理
原文:https://www.cnblogs.com/azhao/p/11881255.html