首页 > 代码库 > template_11实参演绎

template_11实参演绎

1,演绎过程
技术分享
匹配类型A(来自实参的类型),参数化类型P(行参参数声明)
如果被声明的参数是一个引用声明g(T& )那么P就是所引用类型T;
f(T)中P就是所声明的参数类;
decay指从数组和函数类型隐式转换为指针类型。
如果实参的类型是数组或函数类型,则会发生decay,此时还会忽略高层次的const和volatile限定符。


template <class T>
T const& max(T const& a, T const& b)

技术分享


技术分享
T被要求同时是char[7]和char[4]所以error.
如果去掉参数声明中的引用符号,则可以decay,演绎成char*就能编译通过了。


2,演绎上下文
根据一些基本的类型匹配构造出复杂的类型。

技术分享
fp中T演绎为int**
fe中E为bool,N为32
fs中T1,T2,T3为int,S,double

复杂的类型声明都是产生自比它基本的构造;匹配过程从最顶层的构造开始,然后不断递归各种组成元素(即构造):大多数的类型声明构造都可以使用这种方式进行匹配,这些构造被称为演绎 的上下文。

某些构造不能作为演绎上下文:
受限制的类型名称,诸如Q<T>::X类型名称不能被用来演绎模板参数T。
除了非类型参数外模板参数还包含其他成份的非类型表达式,诸如S<T+2>,int(&)[sizeof(S<T>)]。


3,特殊的演绎
存在两种特殊情况,其中用于演绎的实参-参数对(A-P)并不是分别来自于函数调用的实参和函数模板的参数。
第1种情况出现在取函数模板地址的时候,此时P是函数模板声明的参数化类型,而A是被赋值或初始化的指针所代表的函数类型:
template<class T>
void f(T, T);

void(*pf)(char, char) = &f;

上面P就是void(T, T),而A就是void(char, char)
pf被初始化为"特化f<char>"的地址

另一种特殊情况和转型运算符模拟一起出现,
资料说可以吧S转换为int(&)[20],于是P为T[N],A为int[20]
but,我测试却是无法转换,可能是vs不支持。

技术分享


4,
模板实参演绎只能应用于函数模板和成员函数模拟,不能用于类模板和类模版的构造函数。
技术分享


依赖于模板参数调用缺省实参
技术分享


不是依赖型,也不能用于演绎模板实参
技术分享


5,Barton-NackMan方法:限制的模板扩展
需要定义类模版的operator ==的时候,如果把该运算符声明为模板的成员,则改运算符的第一个实参(this指针)和第二个实参的转型规则可能不一致。而operator==意味着它的两个实参应该是对称的,有了不同转型后就很难保证这种对称性了 。
于是如下图把该运算符声明为一个名字空间作用域的函数供调用。

技术分享



如果函数模板不能被重载,则在这个作用域中也不能声明其他的operator==模板了。
把这个运算符作为类的普通友元函数定义在类的内部即可。
假设用int类型来实例化模板类,那么作为实例化的结果,这个友元类运算符相应地被具体声明了(即确定了参数类型)。但是这个具体函数本身并不是函数模板实例化的结果,它本身就是一个非模板函数,只是借助于实例化过程的边缘效应它才被声明为一个具体函数,并且插入到全局作用域中。
由于是非模板函数,所以即使在语言不支持函数模板重载的情况下也可以对该运算符"重载",借助该技术可以不使用模板运算符operator==(T, T)却使运算符能应用于所有类型的T(无限扩展机制),因此叫做限制的模板扩展(restricted template expansion)。
由于友元定义在类定义的内部,因此它被隐式的看作是inline的,因此可以把实现委托给一个函数模板,这个函数模板不需要内联也不会和相同名字的其他模板冲突。

技术分享



技术分享
模板运算符重载:        Arry<>::operator==
运算符友元:            operator==
命名空间作用域运算符:    operator==<>

-------------------------------------------------
该技术的关键就是在类模板实例化的过程中,伴随生成一个非模板的具体函数。
而且这个函数并不产生自函数模板,因此也不需要去进行模板实参演绎,但该函数却属于重载解析规则。


技术分享

template_11实参演绎