类模板默认参数必须从右往左连续提供,所有带默认值的参数须位于参数列表末尾;偏特化不继承默认值;默认值须在实例化时可解析,不可用未定义别名或非法表达式。

类模板默认参数必须从右往左连续提供
类模板的默认模板参数不是函数参数,不能跳着设。比如 template<typename t typename u="int," v="double"></typename> 合法,但 template<typename t="void," typename u v="double"></typename> 编译失败——因为 U 没默认值却排在有默认值的 V 左边。
常见错误现象:error: default template arguments may not be used in partial specializations 或更隐蔽的“找不到匹配的模板实例化”,往往就卡在这条顺序规则上。
- 所有带默认值的模板参数必须放在参数列表末尾
- 偏特化时不能引用默认参数(偏特化本身不继承主模板的默认值)
- 如果想让某个中间参数可选,只能把它挪到参数列表靠后位置,前面用占位类型兜住
std::vector 是典型默认参数实践案例
看 std::vector 的声明:template<class t class allocator="std::allocator<T">></class>。这里 Allocator 默认用了 std::allocator<t></t>,既满足类型推导需求,又不强制用户每次写全。
使用场景:你自己写容器类或策略类时,把可插拔组件(如内存分配器、比较器、哈希器)设为带默认值的模板参数,能极大提升易用性。
立即学习“C++免费学习笔记(深入)”;
- 默认值可以是具体类型(如
int),也可以是依赖前序参数的表达式(如std::allocator<t></t>) - 但不能是未定义的别名或未声明的模板(比如提前用
using Alloc = ...但没在作用域里) - 注意:默认值里的类型必须在实例化时可完整解析,否则链接期可能报错,而非编译期
别在模板参数里默认构造函数参数
有人试图这么写:template<typename t defaultval="T{}"></typename>,以为能给类型 T 设个默认值。这看似合理,实则危险。
问题在于:不是所有 T 都支持 T{}(比如没有默认构造函数的类、引用类型、抽象类),而且这个默认值会参与模板实例化——哪怕你根本没用到它。
- 编译器会在看到模板声明时就尝试检查
T{}是否合法,导致本不该触发的编译失败 - 更稳妥的做法是把默认逻辑下沉到类内部(比如构造函数里设成员默认值),而不是塞进模板参数
- 若真需要运行时默认行为,考虑用
std::optional<t></t>或额外的非类型模板参数(C++20 起支持auto非类型参数)
显式指定部分模板参数时,省略的只能是末尾那些
假设你定义了 template<typename t typename u="char," v="void"></typename>,那么以下写法合法:
MyClass<int> a; // T=int, U=char, V=void MyClass<int, bool> b; // T=int, U=bool, V=void
但 MyClass<int, , void> 或 MyClass<int, _, void> 这种“跳过中间”的写法不存在——C++ 不支持参数占位符。
- 想只改最后一个参数?必须把前面的都写出来:
MyClass<int, char, float> - 如果参数太多,考虑用参数包封装(
template<typename... args></typename...>)+ 辅助类型推导,或者拆成嵌套模板 - IDE 可能提示“expected a type”之类错误,其实只是你漏写了某个必填参数,别急着查语法,先数清楚写了几个
默认参数看着省事,但一旦涉及类型依赖、偏特化或跨编译单元使用,很容易变成隐性耦合点。最麻烦的是——错误常不在声明处爆发,而在某个看似无关的实例化位置突然炸开。











