模板类实现必须放在头文件中,因编译器需在实例化点看到完整定义以生成代码;分离声明与实现会导致链接错误;全特化与偏特化需区分,函数模板不支持偏特化。

模板类定义时必须把实现写在头文件里
因为编译器需要在实例化点(比如 vector<int></int>)看到完整的模板定义,才能生成对应类型的代码。如果把 template<class t> class Stack</class> 的声明放 stack.h,而把成员函数实现放在 stack.cpp,链接时一定会报 undefined reference to Stack<int>::push(int const&)</int>。
常见做法是:头文件里直接写函数体,或者用 inline 修饰,或者把实现放在同名的 .tpp 文件并在头文件末尾 #include "stack.tpp"。
- 不要试图在 .cpp 中显式实例化所有可能类型(比如
template class Stack<int>;</int>),这不可扩展且易漏 - 使用
export关键字?C++98 曾有但已被弃用,现代标准不支持 - Clang/GCC 都不支持分离编译,别浪费时间试
类模板特化要区分全特化和偏特化
template<class t> class Container</class> 可以针对具体类型做全特化(如 Container<char></char>),也可以对部分类型模式做偏特化(如 template<class t> class Container<t></t></class>)。但注意:偏特化只允许用于类模板,函数模板不支持偏特化 —— 想对函数做类似操作得靠重载或 if constexpr(C++17)。
- 全特化写法:
template class Container<bool> { ... };</bool> - 偏特化写法:
template<class t> class Container<t> { ... };</t></class>,必须保留模板参数列表(哪怕没用到) - 偏特化不能只是“更具体的函数签名”,它必须改变模板参数结构(比如从
T变成T*或T[])
模板参数推导失败时要主动提供类型或加 std::declval
写模板类的构造函数或辅助函数时,常遇到编译器无法从参数反推 T。比如:template<class t> class Wrapper { Wrapper(T val); };</class>,调用 Wrapper{42} 在 C++17 前会失败,因为没有函数上下文供推导。
立即学习“C++免费学习笔记(深入)”;
- 解决办法一:显式指定类型,
Wrapper<int>{42}</int> - 解决办法二:加一个接受任意类型的构造函数模板(注意不是类模板),再用
static_cast<t></t>或std::decay_t转换 - 涉及 SFINAE 判断类型能力时,
std::declval<t>()</t>是安全取值的唯一方式 —— 它不求值、不构造,只用于类型计算
继承模板类时记得用 using 拉入基类成员名
当派生自 template<class t> class Base</class> 时,编译器在解析 Base<t>::value</t> 这类名字前,无法确定它是否依赖于模板参数(即是否是“从属名称”)。默认不查找基类作用域,直接报错 ‘value’ was not declared in this scope。
- 必须写
using Base<t>::value;</t>才能让派生类直接访问 - 或者显式写
this->value或Base<t>::value</t>,但后者在虚函数中可能绑定错误版本 - 别信“加
typename就行”——那是给类型名用的,不是给变量或函数用的









