先提一嘴:本文虽然重点在函数模板,但还是希望各位将目光放在声明模板时的尖括号上。用于后续与其他类别的模板作对比。
正文
众所周知,模板声明部分的尖括号中的内容是声明模板形参,而调用模板时的尖括号是给模板传参。然而这样理解仅仅停留于现象,只是将模板形参传参和函数传参的过程划等号了。C++ 的函数重载匹配并非真的进行匹配,因为函数名修饰规则导致重载总能第一时间找到匹配的函数。而模板不同,对模板传参进行实例化过程中,模板匹配的过程是真实发生的(以后有机会再细说这个过程)。
先谈实例化条件。实例化条件不同,即使模板标识符相同,也会被当作不同模板。对应的就是实例化模板时的匹配规则。这里需要分清实例化条件和匹配规则。匹配规则是根据不同模板所要求的实例化条件,对模板实例化语句进行条件查找,以找到最合适的模板进行实例化。
对于不同类别的模板,对多个同名模板实例化条件的要求也各不相同,实际上对应的就是匹配规则的不同。我们先谈实例化条件的要求,也就是同名模板中尖括号的规则。
1、函数模板规则
函数模板的实例化条件是最宽松的,同样数量的模板形参,但类别不同,依然当作不同模板处理。模板形参个数不同也会被当作不同模板(其中讲类当作模板参数必须 C++20 以上才支持)。这一切都是为了配合函数重载。函数模板支持以类型推导来隐式实例化,也正因为如此,函数模板的规则是最麻烦的一种,它的理解有别于其他模板,甚至可以说与其他模板的实例化规则不是同一套(之后再谈其他类别模板时再说)。
因为函数模板对实例化条件的要求过于宽松,加上模板由于有默认模板形参的存在,类比于函数重载会引发重载歧义,函数模板也会引发模板实例化歧义。
2、为什么函数模板没有偏特化
基于以上所提及的特性,模板没有偏特化的第一个原因:对于指定其中某个模板形参的这种偏特化,函数模板压根不需要这种形式。原因如下图:
这不就是模板偏特化么?只是不支持像类模板那种偏特化写法罢了。
至于第二种偏特化,即对传入类型加以限定的偏特化,许多情况下都会导致编译器无法判断以原始模板还是偏特化进行实例化。先看个例子,虽然这个例子并不贴切。
也就是说,当你不显式指明函数模板形参时,编译器根本无法判断你是想传值还是传引用。假如此时你对模板形参以引用进行限定偏特化,编译器如何判断你传入的右值函数参数是要传值还是传引用?应该选择哪个版本进行实例化?毕竟函数传引用和传值都是特别常见的操作,根本不存在优先级。除了引用问题,const 偏特化也是一样的情况,可自行类比于下图的函数重载歧义。
总结来说函数模板之所以没有偏特化一部分原因是不需要,另一部分原因是做不到。归根结底还是由于函数模板对类型推导的支持所导致。