根据《The C++ Programming Language》,模板的声明和定义可以分别放在.h和.cpp文件中,但是我发现目前GCC和VC都不支持这种代码安排方式,因此一般采用将其声明和实现同时放在一个hpp或者cpp文件中。
模板形参和实参(Template Parameter and Argument):
template<class T> class String;
String<char> str;
String<wchar_t> wstr;
其中T为形参,char和wchar_t为实参。
模板实例化就是根据实参,自动生成模板实例的过程。以上例子就会生成两个模板类String<char>, String<wchar_t>, 相当于根据char和wchar_t将String<T>的代码生成了两份。
实例化的一个重要原则是:未被使用的成员函数将不会被实例化,即最后生成的文件中不包含该模板成员函数。这同时也引出了另外一个问题,编译器在对模板进行语义检查的时候,也不会检查该未使用的成员函数,因此只有其被使用的时候才会检查其错误。
模板的实参可以是:常量表达式, 具有外部链接的对象或函数地址, 非重载的函数指针, 模板。
通过typedef以及常量表达式计算结果相同的参数对应的模板实例相同。比如:
template<class T, int i> class Buffer;
Buffer<char, 10> cbuffer; //具有10个字符的缓冲区,将缓冲区大小作为参数,可以在编译时候确定缓冲区大小,而不需要运行时进行new和delete操作。
typedef unsigned char uchar;
Buffer<unsigned char, 20> 与 Buffer<uchar, 20>等效,是同一个类。
Buffer<char, 20-10> 与 Buffer<char, 10>等效。
使用类模板的时候,都需要显示制定模板实参,但是在使用函数模板的时候,不需要显示指定,编译器会根据传入函数的实参数据类型推导出函数模板的实参(Function arguments->Template arguments),从而完成函数的实例化。
当然使用函数模板也可以显示指定模板实参,全部或者一部分,如果函数模板的部分实参可以根据函数调用推导出来,那么只需要指定剩下不能推导出来的模板实参。这里也有一个跟函数缺省参数类似的原则,就是显示指定的实参只能是位于左边,右边未指定的需要推导。如:
template<class T, class C> T implicit_cast(C c) { return c; }
int i = implicit_cast<int>(2.0); //其实就是调用implicit<int, double>(2.0);
template<class T> T* create() { return new T(); }
int k = create<int>(); //参数是返回值,需要显示指定
与类和函数的规则相似,类模板不允许重载,即一个类模板的名字不允许出现多次(不过模板特殊化的时候例外Specialization),但是函数模板可以重载,通过给定不同的模板形参组合定义多个函数模板。因此在函数模板进行实例化的时候,就有一个Overload Resolution的过程,即如何根据函数实参推导出最合适的函数模板。
template<class T> T sqrt(T);
template<class T> complex<T> sqrt(complex<T>);
double sqrt(double);
complex<double> z(1.0, 2.0);
sqrt(2); //sqrt<int>(int)
sqrt(2.0);//sqrt(double);
sqrt(z); //sqrt<double>(complex<double>);
首先推导出每一个可能的候选模板函数:
如sqrt(z) 得到两个候选:sqrt<double>(complex<double>)和sqrt<complex<double>>(complex<double>)
优先顺序:
1. 普通函数。如:sqrt(double)优于sqrt<double>(double);
2. 最特殊化的,如:template<class T> complex<T> sqrt(complex<T>);比template<class T> T sqrt(T);更加特殊化,所以前者优先。
解决歧义的方法:
1. 显示指定模板参数
2. 进一步重载,不过以内联函数的方式,避免多级别的函数调用带来的开销。如:
template<class T> T max(T, T);
max(1, 2.0); //产生歧义,max<int>(int, int) or max<double>(double, double)?
解决方法:
1. max<double>(1, 2.0)
2. inline double max(int, double) { return max<double>(1, 2.0); }