函数模板
隐式实例化与显式实例化
常规的模板函数声明如下:
1 | template <typename T> |
模板实际上并不是一个可以直接调用的函数,只有首次使用函数时,编译器才根据参数类型生成一个对应版本的函数实例,然后调用该函数,这个过程称为隐式实例化。C++ 11新增显式实例化:
1 | template int add(int, int); |
该语句提示编译器强制生成一个int版本的模板函数实例,而不管接下来有没有用到该版本的函数。显式实例化可以用于模板函数的声明与定义分离,例如在头文件中声明函数模板,在源文件中定义函数体。
1 | //头文件 声明 |
在模板实现文件中显式具体化后强制生成了对应函数实例,main中调用myswap函数时,编译器虽然无法访问函数定义生成main自己的函数实例,但在链接阶段找到了myswap.obj中签名相符的函数实例,因此链接通过。显式实例化避免了重复实例化,缺点是必须在模板实现文件中实例化所有需要的版本,适用于重载需求较少的模板。
显式实例化与具体化
模板只能解决“普遍情况”下的问题,对于特殊参数类型,通用模板就无法工作。为了解决类似的“具体”问题,需要为对应的参数类型实现“具体化”的模板定义:
1 | template<> char* add(char* pc1, char* pc2) |
显而易见,具体化的模板优先级高于普通模板。如果在同一编译单元同时存在签名相同的显式实例化与具体化,且显式实例化在具体化之前,则会引发重复定义错误,原因是编译器先显式实例化生成了函数实例,但接下来的语句出现了优先级更高的具体化模板,编译器尝试以具体化模板生成函数实例,但已存在相同签名的函数实例,因此引发重复定义错误。反之编译器先以具体化模板为原型生成函数实例,则会跳过优先级较低的显式实例化,编译通过。
1 | //通用模板 |
模板调用优先级
如果同时存在参数类型匹配的非模板函数、普通模板、具体化模板,则非模板 >具体化模板 >通用模板,其中具体化模板参数越具体,其优先级越高。调用时也可以强制使用模板函数,此时只考虑模板的优先级。
1 | int main() |
类模板
偏特化
类模板与函数模板使用方法类似,不过类模板多一个偏特化(或者叫部分具体化)语法:
1 | //通用模板 |
偏特化可以看作是对模板的进一步具体,介于完全具体与完全泛化之间。函数模板没有偏特化语法,因为可以用函数模板重载来实现一样的效果:
1 | template<typename T> |
以上代码测试结果均来自VS2013,具体情形以编译器实现为准