我们现在已经知道了,函数模板有两类参数。
1. 位于函数名之前,在尖括号里声明的模板形参。
template <typename T> // T 就是模板参数
2. 位于函数名之后, 在圆括号里声明的调用形参 (call parameter)。
T max(T a, T b) // a 和 b 是调用参数( call parameter)
可以指定任意个数的模板形参。比如,你可以定义一个 max() 模板, 但它的调用形参不必是同一种类型。
template <typename T1, typename T2> T1 max( T1 a, T2 b) { return b < a ? a : b; } ... auto m = ::max(4,7.2); // 正确 :但是返回值类型由第一个参数决定
如果想给 max() 模板传入不同类型的实参,这么做当然是可以的。但就像上面这个例子显示的,这么做可能会产生一个问题 : 如果你采用两者( T1, T2)之一 作为返回值类型,传进另一个形参的实参可能会被强制转型,而这并不一定是调用者所想的。 因此,返回值类型取决于传入实参的顺序。 max( 66.66, 42) 会返回 double 66.66,而 max(42, 66.66) 返回 int 66。
为解决此问题,C++提供了三种不同的方式:
我们接下来便会讨论这三种方法。
1.3.1 返回类型的模板参数
    之前的讨论表明,模板参数推导允许我们与调用普通函数相同的语法来调用模板函数。也就是说,我们不必显示指定模板参数的类型。
但是,我们还提到,可以明确指出模板参数使用的类型。
template <typename T> T max(T a, T b); ... ::max<double>(4, 7.2); // 把 T 作为 double 类型进行实例化
如果模板和调用参数之间没有联系,而且无法确定模板参数,那就必须通过调用显示指定模板参数。例如,可以引入第三种模板参数来定义函数模板的返回类型:
template <typename T1, typename T2, typename RT>
RT max(T1 a, T2 b);
但是,模板参数推导不考虑返回值类型。(推导可以被看成是重载函数解析的一部分,毕竟函数重载也不依赖于返回值类型。但有个例外——对运算符成员进行转型。) 而且 RT 并没有出现在调用参数里,因此 RT 不能被推导出来。( 在 C++ 里,返回类型也不能从上下文中推导出来。)
因此,必须明确指定模板参数列表,例如:
template <typename T1, typename T2, typename RT> RT max(T1 a, T2 b); ... ::max<int, double, double>(4, 7.2); // 正确,但很麻烦
到目前为止,我们已经研究了函数模板的所有参数被指定或者没有模板参数被指定的情况。另一种方法是只显示指定第一个参数,并允许推导出其余参数。一般来说,你必须指定所有不能隐式确定的参数类型。因此,如果你在样例代码里改变了模板参数的顺序,调用者只需要指定返回类型:
template <typename RT, typename T1, typename T2> RT max(T1 a, T2 b); ... ::max<double>(4, 7.2); // 正确:返回值类型是 double,T1 和 T2 能被推导出来
在上面这个例子里,对 max<double> 的调用将 RT 显式地设置成 double ,但是参数 T1 和 T2 是推导出来的。
请注意,max() 的这些改良版本并不会带来明显的好处。 对于只有一个模板参数的版本,你已经能够指定形参类型和返回值类型——如果传进两个不同类型的实参。 因此,最好还是让模板简单点,使用只有一个模板参数的 max() 版本。 我们在接下来讨论其他模板问题时,就会这么做。
可以前往第 15 章来了解模板推导的详细信息。
原文:https://www.cnblogs.com/rileyye/p/12906190.html