模板模板参数用于接收类模板而非特化类型,声明必须为template class/typename,如template;std::vector可传入而std::vector不可,因前者是模板名后者是类型;多参数模板需用template匹配但丧失静态检查;默认参数不影响匹配,但形参类型(类型/非类型)须严格一致;偏特化时签名必须完全匹配,否则易漏判,建议优先用sfinae或std::is_detected提升可移植性。

模板模板参数的基本声明语法
模板模板参数不是“模板的模板”,而是用来接收另一个模板作为实参的参数,常见于容器适配器、类型萃取或元编程中。它必须显式声明为 template<...> class</...>(C++17 起也允许 template<...> typename</...>),不能写成 class T 或 template<class> T</class> 这类错误形式。
正确写法示例:
template<template> class Container>
struct Adapter { /* ... */ };</template>
这里 Container 是模板模板参数,它期待一个接受单个类型参数的类模板,比如 std::vector、std::list;但不能传 std::map(它要两个参数)或 std::vector<int></int>(这是实例化后的类型,不是模板)。
为什么 std::vector 能传进去,而 std::vector<int></int> 不行
模板模板参数绑定的是“模板名”本身,不是特化后的类型。传 std::vector<int></int> 会报错,典型错误信息是:error: type/value mismatch at argument 1 in template parameter list。
-
Adapter<:vector></:vector>✅ 合法:传的是模板名 -
Adapter<:vector>></:vector>❌ 错误:这是类型,不是模板 -
Adapter<:map></:map>❌ 错误:std::map接受Key, T, Compare, Alloc四个参数,签名不匹配
若想支持多参数模板(如 std::map),需用占位符包住所有参数:template<template> class C></template>,再配合变参展开,但要注意:这会失去参数数量和类型的静态检查能力。
组合式C++代码评审方案,融合静态分析、AI推理、多轮迭代评审和C++专项检查,适用于PR审查、增量代码审查、全项目评审和代码质量评分,触发词包括review cpp、cpp代码评审、C++review、代码审查。
带默认模板参数的模板模板参数怎么处理
如果被传入的模板自身有默认参数(如 std::basic_string<char std::char_traits>, std::allocator<char>></char></char>),只要模板模板参数的声明能覆盖其最简形式,就可匹配。但注意:编译器只比对模板形参列表结构,不展开默认值。
-
template<template> class T> struct X {};</template>可接受std::vector(它定义为template<class t class alloc="allocator<T">> class vector</class>),因为前导class T匹配 - 但无法接受
std::array(它形参是template<class t size_t n> class array</class>)——第二个参数不是类型,是非类型,必须显式写成template<class t auto n></class>或分开声明 - C++20 起可用
template<class t auto n></class>捕获非类型参数,但模板模板参数本身仍不能直接混用类型与非类型形参
实际踩坑:别忘了模板模板参数也能偏特化
模板模板参数常被用于偏特化,但偏特化时必须严格匹配模板签名。例如:
template<template> class C> struct is_container : std::false_type {};
template<template> class C> struct is_container<c> : std::true_type {};</c></template></template>
上面第二行不会触发——因为 std::vector 的签名不是 template<class...></class...>,而是 template<class class></class>。真正有效的写法是:
template<template class> class C> struct is_container<c> : std::true_type {};</c></template>
更健壮的做法是放弃模板模板参数,改用 SFINAE 或 std::is_detected 检测嵌套类型(如 value_type),否则容易因模板参数个数/类型/默认值差异漏匹配。
真正难的不是写对声明,而是预判目标模板的完整签名——尤其是标准库实现可能在不同 STL 版本间微调参数顺序或默认值,这时候硬绑模板模板参数反而降低可移植性。
C++免费学习笔记(深入):立即使用
在学习笔记中,你将探索 C++ 的入门与实战技巧!










