C++ 模板显式实例化
头文件:template.hpp
#ifndef TEMPLATE_HPP
#define TEMPLATE_HPP
template <typename T>
void println(T t);
#endif
源文件:main.cpp
#include "template.hpp"
int main() {
println(9);
println(-7);
}
源文件:instantiation.cpp
#include <iostream>
#include "template.hpp"
template <typename T>
void println(T t) {
std::cout << t << "\n";
}
// 特化声明:失败,因为没有定义
// template <>
// void println<int>(int);
// 显式实例化:成功
template void println<int>(int);
// template void println(int); // 如果能够从函数参数中推导出模板参数,也可以省略模板参数
// 特化定义:成功
// template <>
// void println<int>(int t) {
// std::cout << "instantiation.cpp: " << t << "\n";
// }
翻译单元 A 要调用模板函数:
- 如果它能看到模板函数的特化定义,就会直接使用。
- 如果它只能看到模板函数的定义(即便是看到了特化函数的声明也不行),但是看不到特化的定义,就会去生成。
- 如果只能看到模板函数的声明,则没办法生成,就只能等待链接。
所以想要节省编译时间只能在头文件中留下声明,不能留定义。
在实例化的时候也要注意,首先是对每一个类型都写一遍函数体太累了,可以直接在源文件中提供定义,然后显式实例化或者特化。两者关系如下:
- 显式实例化和特化不能一起出现。显式实例化在特化之前则会报错,在特化之后会没有效果(相当于没有使用显式实例化)。
- 写法上有区别:注意特化是要加
template <>
的,而显式实例化是加的template
,没有<>
。 - 显式实例化不能提供定义(函数体),特化可以有定义也可以没有(在和显式实例化进行对比的这种场景下,我们希望特化有定义,否则函数无法生成,链接不能成功)。
Warning
特化函数只声明不定义没有意义。如果所在翻译单元能看到 primary 模板的定义,则会自己生成,如果看不到则会延迟到链接阶段去解决符号查找问题,是否有特化函数声明没有影响。
为什么需要显式实例化?因为模板实例化是按需进行的,显式实例化就是告诉编译器这个函数之后会被用到,所以先实例化出来方便链接。