C++模板匹配过程

只是从语法上分析,并没有看(也看不了)编译器源码

Posted by TB on October 23, 2019

C++模板匹配过程

参考链接

示例代码如下

template <typename T> struct DoWork;	      // (0) 这是原型

template <> struct DoWork<int> {};            // (1) 这是 int 类型的特化
template <> struct DoWork<float> {};          // (2) 这是 float 类型的特化
template <typename U> struct DoWork<U*> {};   // (3) 这是指针类型的偏特化

DoWork<int>    i;  // (4)
DoWork<float*> pf; // (5)

编译器解析过程



					(1)struct DoWork<int>
					(2)struct DoWork<float>
struct DoWork (0) --->  //特化
//原型                
    				 (3)template <typename U> struct DoWork<U*> {};//指针类型的偏特化
    				 

编译器会维护两个字典 , 称为TemplateDict 储存着模板原型

第二个是 TemplateSpecDict 储存着模板的特化形式

TemplateDict[DoWork<T>] = 
{
    DoWork<int>,
    DoWork<float>,
    DoWork<U*>                     
};

在实例化(4)时会发生

//伪代码
templatePro = TemplateDict.find(DoWork) //先找到函数原型
template TemplateSpecDict.match(int)//在特化/偏特化表寻找int


看以下示例

template <typename T1, typename T2> struct X  ;//模板原型

template <typename T1>			struct X<T1,int*> (1)
template <typename T1>           struct X<int,T1>  (2)//其实这样写是错误的
template <>           			struct X<int,int*>(3)

template <typename T>            struct X<unique_ptr<T>,share_ptr<T> >; 
....
do something
....
X test<int , int*>

X会匹配到第三个 也就是和<int , int*>完全一致的(偏)特化模板

很符合直觉认知,但是为啥?

首先 特化和偏特化是可以嵌套的

其次偏特化时的模板形参,和原型的模板形参没有任何关系。和原型不同,它的顺序完全不影响模式匹配的顺序

最短路径寻找参数

在编译试图匹配第二个模板参数时 , 会尝试找到最接近给定参数的模板作为一个模式匹配,偏特化的实参列表中展现出来的“样子”,就是它能被匹配的原因。比如,struct X<T, T>中,要求模板的两个参数必须是相同的类型。而struct X<T, T*>,则代表第二个模板类型参数必须是第一个模板类型参数的指针,比如X<float***, float****>就能匹配上。

​ —参考资料

这样理解 , 当编译器尝试匹配一个指针int*时 此时

TemplateDict[X<T>] = 
{
    X<T*>;
    X<T>;    
};

此时会匹配到T* 因为 int* 更加接近于T*

同时 其他的类模板也可以作为偏特化时的“模式”出现,例如示例8,它要求传入同一个类型的unique_ptrshared_ptr

N3337, 14.8.2.5/8

T是模板类型实参或者类型列表(如 int, float, double 这样的,TT是template-template实参(参见6.2节),i是模板的非类型参数(整数、指针等),则以下形式的形参都会参与匹配:

T,cv-list T,T*, template-name <T>, T&, T&&
T [ integer-constant ]
type (T), T(), T(T)
T type ::*, type T::*, T T::*
T (type ::*)(), type (T::*)(), type (type ::*)(T), type (T::*)(T), T (type ::*)(T), T (T::*)(), T (T::*)(T)
type [i], template-name <i>, TT<T>, TT<i>, TT<>

刚刚在前面还写了这样一个

template <typename T1>			struct X<int, T1*>;//(1)

template <typename T1>           struct X<T1, int*>;//(2)

前面有提到

模板形参,和原型的模板形参没有任何关系。和原型不同,它的顺序完全不影响模式匹配的顺序

要这么写 编译器就会不知道应该匹配到哪一个特化模板 , 从而导致错误了