包含类型
按照传统的C++开发习惯,非模板代码会将类和其他类型声明放在头文件中(.hpp或.h等),而全局变量和(非内联)函数的定义则放置在单独的实现文件(.cpp)中。这种方式确保了类型在整个项目中可见,并防止链接时出现重复定义的错误。
然而,在模板的实际使用中,这种分离声明和定义的做法会导致问题。例如,有一个名为printTypeof的模板函数,它的声明位于头文件myfirst.hpp中,而实现却在单独的myfirst.cpp文件中。当在其他源文件中包含头文件并对printTypeof函数模板进行实例化时,编译器只能看到模板的声明,并假设在其他地方存在该模板的实现。但是在链接阶段,编译器找不到为特定类型(如double)实例化的函数定义,导致链接器报错,找不到函数定义。
解决这个问题的标准做法是将模板函数的声明和定义都放在同一个头文件中,这样在包含该头文件的地方就可以自动实例化模板函数,从而避免链接错误。这就是所谓的“包含模型”。这种做法类似于内联函数和宏,确保模板函数实例化发生在每个使用模板的翻译单元中。
但这种方式导致的一个问题就是,这种包含模型相当于是编译期复制了更多的代码,增加了编译器编译重要程序所需的时间。一种较好的解决方式是引入C++20的模块机制。
模板与内联函数
模板函数默认情况下不一定像普通内联函数那样在调用处展开,为了提高性能和避免不必要的实例化副本,常常需要将函数模板声明为内联函数。内联只是对编译器的一种请求,并不保证一定会内联展开,编译器会根据实际情况作出最优决策。