相应型别(associated types)
一个变量的型别即这个变量的类型,比如 int a = 5; 则变量 a 的型别为 int 。
一个指针的相应型别即所指对象的型别。比如 int *pa = &a; 则变量 pa 的相应型别为 int 。
如果在函数中想定义一个与实参的相应型别一样的临时变量该如何处理?
比如 在 void func(pa); 中想定义一个与 pa 的相应型别相同的变量;如上,pa 的相应型别为 int ,当然只需要直接定义即可。
但是如果 pa 不是内置类型,又该如何处理?毕竟 c++ 只支持 sizeof(),并未支持 typeof() !即便有,我想返回的应该也是字符串,不能用于变量声明。
解决的办法是利用 function template 的参数推导 ( argument deducation )机制。
template<class T1,class T2>
void fun_in(T1 t1,T2 t2)
{
T2 tmp;//这里T2即为t1所指对象型别
...//fun的工作
}
template<class T1>
void fun(T1 t1)
{
fun_in(t1,*t1);
//fun的工作由fun_in完成
}
编译器会自动进行template参数推导,于是可以导出型别T2,顺利解决问题。
如果上面的型别T2是用于函数返回值又该如何处理?
声明内嵌型别可以解决。
template<class T2>
struct T1
{
typedef T2 value_type;//内嵌型别声明
T2 *ptr;
T1(T2 *p = 0):prt(p){}
T2& operator*() const
{ return *ptr; }
...//
};
template <class T1>
typename T1::value_tpye//此处时fun的返回值型别
fun(T1 t1){ return *t1; }
...//
T1<int> t1(new int(1)); //此处T2即为int
cout<<fun(t1);
fun(T1 t1) 的返回值型别必须加上关键字 typename,因为T2是一个 template 参数,在它被编译器具现化前,编译器对T2一无所知。
即编译器并不知道T1<T2>::value_type 代表的是一个型别还是一个成员函数或数据成员。关键词typename告知编译器是一个型别。
上面看到T1需要是一个类别我们才可以这么处理,如果T1只是一个普通的指针,比如int*,该如何处理?也许会认为普通指针是内置
类型,直接为int fun(int *t1); 即可。确实如此,但是有时候这样却无法处理。比如STL中的迭代器iterator;对于list<T>而言,iterator的相应
型别为__list_node<T>;iterator为模板类,如上处理即可。但是对于vector<T>,iterator允许是普通的指针T*,尤其当T是内置类型的时候。
为了让iterator与容器无关,即不管是vector还是list的iterator,都能统一处理上述问题。故需要针对普通指针对特殊处理。可以采用
template partial specialization。
template<class T1>
struct T1_traits {
typedef typename T1::value_type value_type;
}; //此版本接受T1为任何型别
template<class T1>
struct T1_traits<T1*>{
typedef T1 value_type;
}; //此特化版本仅适用于T1为普通指针情况
于是fun可以改写为:
template <class T1>
typename T1_traits<T1>::value_type
fun(T1 t1)
{ return *t1;}
还需要注意一点,如果T1为指向常数对象的指针,那么特化版本T1_traits<const int*>::value_type得到的value_type为const int,则不能用于声明临时
变量,因为无法赋值。所以有必要再特化另一个版本
template<class T1>
struct T1_traits<const T1*>{
typedef T1 value_type;
}; //
因此,不管T1是类类别,还是普通指针,或普通常指针,都可以一并处理。
上述内容参考《STL源码分析》Traits编程技法一节。