本节书摘来自异步社区出版社《c++ templates中文版》一书中的第2章,第2.3节,作者: 【美】david vandevoorde , 【德】nicolai m. josuttis,更多章节内容可以访问云栖社区“异步社区”公众号查看。
函数模板有两种类型的参数。
1.模板参数:位于函数模板名称的前面,在一对尖括号内部进行声明:
2.调用参数:位于函数模板名称之后,在一对圆括号内部进行声明:
你可以根据需要声明任意数量的模板参数。然而,在函数模板内部(这一点和类模板有区别),不能指定缺省的模板实参[4]。例如,你可以定义一个“两个调用参数的类型可以不同的”max()模板:
这看起来是一种能够给max()模板传递两个不同类型调用参数的好方法,但在这个例子中,这种方法是有缺点的。主要问题是:必须声明返回类型。对于返回类型,如果你使用的是其中的一个参数类型,那么另一个参数的实参就可能要转型为返回类型,而不会在意调用者的意图。c++并没有提供一种“指定并且选择一个‘最强大类型’”的途径(然而,你可以使用一些tricky模板编程来提供这个特性,详见15.2.4小节)。于是,取决于调用实参的顺序,42和66.66的最大值可以是浮点数66.66,也可以是整数66。另一个缺点是:把第2个参数转型为返回类型的过程将会创建一个新的局部临时对象,这导致了你不能通过引用[]来返回结果。因此,在我们的例子里,返回类型必须是t1,而不能是t1 const&。
因为调用参数的类型构造自模板参数,所以模板参数和调用参数通常是相关的。我们把这个概念称为:函数模板的实参演绎。它让你可以像调用普通函数那样调用函数模板。
然而,如前所述,针对某些特定的类型,你还可以显式地实例化该模板:
当模板参数和调用参数没有发生关联,或者不能由调用参数来决定模板参数的时候,你在调用时就必须显式指定模板实参。例如,你可以引入第3个模板实参类型,来定义函数模板的返回类型:
然而,模板实参演绎并不适合返回类型[6],因为rt不会出现在函数调用参数的类型里面。因此,函数调用并不能演绎出rt。于是,你必须显式地指定模板实参列表。例如:
到目前为止,我们只是考察了显式指定所有函数模板实参的例子,和不显式指定函数任何模板实参的例子。另一种情况是只显式指定第一个实参,而让演绎过程推导出其余的实参。通常而言,你必须指定“最后一个不能被隐式演绎的模板实参之前的”所有实参类型。因此,在上面的例子里,如果你改变模板参数的声明顺序,那么调用者就只需要指定返回类型:
在这个例子里,调用max< double >时显式地把rt指定为double,但其他两个参数t1和t2可以根据调用实参分别演绎为int和double。
可以看出,所有这些修改后的max()版本都不能得到很大的改进。由于在单(模板)参数版本里,如果传递进来的是两个不同类型的实参,你已经可以指定参数的类型(和返回类型)。因此,尽量保持简洁并且使用单参数版本的max()就是一个不错的主意(在接下来的几节里,当讨论其他模板话题的时候,我们将使用这种方法)。