原来实现一个函数很容易,比如
int max (int a, int b)
{
// 如果 a<b 就传回 b,否则传回 a
return a < b ? b : a;
}
但现在a和b是double类型,上面的函数就不能用了,有几种方法解决,最简单的就是再写一个,如
double max (double a, double b)
{
// 如果 a<b 就传回 b,否则传回 a
return a < b ? b : a; //这里暂时不考虑精度问题
}
那么问题来了,第2个函数是在做重复的事情,万一第1个函数有错误的话,之后不同类型的函数也会犯下相同的错误,
而且有可能不敢使用更复杂但更好的算法,因为这样会有更多的错误。
templates可以解决以上的问题,Templates如今已被大量运用。就拿C++标准库来说,几乎所有程序代码都以templates写成。
标 准库提供各式各样的功能:对objects和values排序的各种算法、管理各种元素的数据结构(容
器类别,container classes)、支持各种字符集的string(字符串)。
定义Template
以上max函数可以这样定义,
template <typename T>
inline T const& max (T const& a, T const& b)
{
// 如果 a<b 就传回 b,否则传回 a
return a < b ? b : a;
}
可以使用关键词 class 代替关键词 typename,就语意而言,前后两者毫无区别。
使用Template
定义了max函数,就可以使用了,
int main()
{
int i = 42;
std::cout << "max(7,i): "<<::max(7,i)<<std::endl;
double f1 = 3.4;
double f2 = -6.7;
std::cout << "max(f1,f2): "<< ::max(f1,f2) << std::endl;
std::string s1 = "mathematics";
std::string s2 = "math";
std::cout << "max(s1,s2): "<< ::max(s1,s2) << std::endl;
}
程序对max()的三次调用都加了前缀字 “::”,以便确保被调用的是在全局命名空间 中定义的max()。
标准库内也有一个std::max() template,可能会在某些情 况下被调用,或在调用时引发模棱两可。
输出结果如下:
max(7,i): 42
max(f1,f2): 3.4
max(s1,s2): mathematics
这里有个问题,就是templates没有实例化,注意只要function template 被使用,就会自动引发实例化过程。
上面函数三次调用都成功了,三个类型都支持函数里的操作”<”,如果不支持就会编译错误
std::complex c1, c2; // 此类型并不提供 operator<
…
max(c1,c2); // 编译期出错
自变量推导
上面已经成功运用templates了,然后如果a和b的类型不一样呢
template <typename T>
inline T const& max(T const& a, T const& b);
...
max(4, 7); // OK,两个T都被推导为int
max(4, 4.2); // 错误:第一个T被推导为int,第二个T被推导为double
遇到这种情况,有3种方法解决
- 把两个自变量转型为相同类型:max(static_cast<double>(4), 4.2);
- 明确指定 T 的类型:max<double>(4, 4.2);
- 对各个 template parameters 使用不同的类型:template <typename T1, typename T2>
重载
最后,函数存在重载,当然function templates 也可以被重载
inline int const& max (int const& a, int const& b)
{
return a < b ? b : a;
}
// 传回两任意类型的数值中的较大者
template <typename T>
inline T const& max (T const& a, T const& b)
{
return a < b ? b : a;
}
// 传回三个任意类型值中的最大者
template <typename T>
inline T const& max (T const& a, T const& b, T const& c)
{
return ::max (::max(a,b), c);
}
int main()
{
::max(7, 42, 68); // 调用「接受三个自变量」的函数
::max(7.0, 42.0); // 调用 max<double>(经由自变量推导)
::max('a', 'b'); // 调用 max<char>(经由自变量推导)
::max(7, 42); // 调用「接受两个 int 自变量」的 non-template 函数
::max<>(7, 42); // 调用 max<int>(经由自变量推导)
::max<double>(7, 42); // 调用 max<double>(无需自变量推导)
::max('a', 42.7); // 调用「接受两个 int 自变量」的 non-template 函数
}
上面例子说明non-template function可以和同名的function template共存,
当其它要素都相等时,重载解析机制会优先选择 non-template function,
而不选 择由 function template 实例化后的函数实体。
还有要把所有形式的重载函数写在 它们被调用之前,否则调用的函数选择可能不一样
template <typename T>
inline T const& max (T const& a, T const& b)
{
return a < b ? b : a;
}
// 传回三个任意类型值的最大者
template <typename T>
inline T const& max (T const& a, T const& b, T const& c)
{
return ::max (::max(a,b), c);
// 即使自变量类型都是 int,这里也会调用 max()template。因为下面的函数定义来得太迟。
}
// 传回两个 ints 的较大者
inline int const& max (int const& a, int const& b)
{
return a < b ? b : a;
}
本文的内容出自C++Templates 侯捷简体中文版