模板类需先写最简可编译版本,声明与定义通常置于同一头文件;偏特化须完整声明且不得有默认参数;全特化优先级最高,但必须在主模板后声明且同命名空间。

模板类怎么写,先看最简能跑通的版本
能编译、能实例化、成员函数能调用,就是模板类写对了的第一关。别一上来就塞 SFINAE 或 requires,先确保基础结构没毛病。
常见错误现象:error: 'T' was not declared in this scope(漏了 template<typename t></typename> 声明)、undefined reference to 'MyClass<int>::func()'</int>(定义和声明分离时,实现没放头文件里)。
- 模板类声明和定义通常得写在同一个头文件里,因为编译器需要看到完整定义才能实例化
- 成员函数如果写在类外,必须重复写
template<typename t></typename>,且作用域要写全,比如:template<typename t> void MyClass<t>::func() { ... }</t></typename> - 别用
typedef替代using做类型别名——后者在模板里更稳定,尤其涉及依赖类型时
偏特化为什么不能只特化函数,而必须特化整个类
因为 C++ 标准禁止「仅对成员函数做模板偏特化」。你看到的“函数级特化”,其实是全类偏特化后附带的成员实现,或者靠 if constexpr / 重载 / enable_if 实现的逻辑分支。
使用场景:想让 MyClass<:vector>></:vector> 行为和普通 T 不同,但又不想为每个 T 写全特化。
立即学习“C++免费学习笔记(深入)”;
- 偏特化必须是类模板的完整声明,语法形如:
template<typename t> class MyClass<:vector>> { ... };</:vector></typename> - 偏特化不能有默认模板参数,也不能省略任何未被“固定”的参数
- 多个偏特化之间不能有歧义,比如
MyClass<int></int>同时匹配MyClass<t></t>和MyClass<int></int>的全特化?编译器会报错,不选
全特化和偏特化的调用优先级怎么判断
编译器按「最特化」原则匹配:全特化 > 偏特化 > 主模板。但这个“最”不是模糊感觉,而是有明确偏序规则。
容易踩的坑:MyClass<int></int> 没走全特化,反而进了偏特化——往往是因为全特化声明位置靠后,或命名空间没对齐。
- 全特化必须在主模板定义之后声明,且不能在函数体内
- 全特化和主模板必须在同一个命名空间;跨命名空间的特化不被识别
- 偏特化之间不可比“谁更特化”,只要两个偏特化都能匹配某个实参,就是二义性错误,比如同时存在
MyClass<t></t>和MyClass<t></t>,而你传入int*,编译失败
模板参数推导失败的典型原因和绕过方式
不是所有地方都能自动推导模板参数,尤其是构造函数、友元函数、返回值类型中。推导失败直接导致编译不过,不是警告。
性能影响:过度依赖 auto 或 decltype 推导可能掩盖类型膨胀,尤其在模板嵌套深时,错误信息会变得极长。
- 构造函数不参与模板参数推导(C++17 前),所以
MyClass m{42};无法推导T,得写MyClass<int> m{42};</int>或加推导指引(deduction guide) - 函数模板参数若出现在返回类型里(如
auto func() -> SomeType<t></t>),无法推导T,必须显式传参或改用参数占位 - 用
std::declval做类型计算时,注意它不求值,但会触发 SFINAE;误用可能让错误信息跳转到完全无关的模板栈帧
模板的复杂性不在语法多难,而在约束传播和匹配顺序是静态、隐式、不可调试的。一个偏特化没生效,往往不是写错了,而是它根本没被编译器“看见”——检查头文件包含顺序、命名空间、以及是否在主模板定义之后才声明特化。










