可变参数模板是C++11引入的特性,通过参数包(typename.../Args...)和展开操作(...)支持任意数量类型与参数;核心用法包括sizeof...获取数量、递归分解处理单个参数、C++17折叠表达式简化展开,广泛用于tuple、variant等标准库组件。

可变参数模板是C++11引入的重要特性,用于编写能接受任意数量、任意类型参数的模板函数或类。核心在于参数包(parameter pack)和展开操作(...)。
基本语法:声明与展开
用typename...或class...声明类型参数包,用Args...声明形参包。展开时args...会将参数依次代入上下文。
最简示例:打印参数个数
template<typename... Args>
void print_count() {
constexpr size_t n = sizeof...(Args); // 编译期获取类型数量
std::cout << "Number of types: " << n << '\n';
}
template<typename... Args>
void print_args_count(Args&&... args) {
constexpr size_t n = sizeof...(args); // 编译期获取实参数量
std::cout << "Number of arguments: " << n << '\n';
}
递归展开:处理每个参数
由于不能直接遍历参数包,常用“头+尾”递归方式逐个处理。需提供非可变版本作为递归终止条件。
立即学习“C++免费学习笔记(深入)”;
- 定义空参数包的终止重载
- 定义至少一个参数的主模板,分离第一个参数(
first)和剩余参数包(rest...) - 在函数体内使用
first,再递归调用自身处理rest...
// 终止重载:空参数包
void print() {
std::cout << '\n';
}
// 主模板:至少一个参数
template<typename T, typename... Args>
void print(const T& first, const Args&&... rest) {
std::cout << first << ' ';
print(rest...); // 递归展开剩余参数
}
// 使用
print(42, "hello", 3.14, true); // 输出:42 hello 3.14 1
折叠表达式(C++17):更简洁的展开方式
替代递归,用(expr ...)或(... expr)一次性展开并组合所有参数,支持+、&&、<<等运算符。
-
(std::cout << ... << args):左折叠,等价于((std::cout << a1) << a2) << ... -
(args + ...):右折叠,求和(要求所有类型支持+) -
(args && ...):逻辑与,全为true才返回true
template<typename... Args>
void print_fold(Args&&... args) {
(std::cout << ... << args) << '\n'; // 自动左折叠
}
template<typename... Args>
auto sum(Args&&... args) {
return (args + ...); // 要求所有参数可相加
}
// 使用
print_fold("a", 1, 3.14); // 输出:a13.14
std::cout << sum(1, 2, 3, 4) << '\n'; // 输出:10
在类模板中使用:存储多个类型/值
可变参数模板类常用于实现元组(std::tuple)、类型列表或配置容器。通常结合继承或成员组合保存参数包。
简易类型容器示例(仅存类型信息,不存值):
template<typename... Types>
struct type_list {};
// 获取类型数量
template<typename T> struct size;
template<typename... Ts>
struct size<type_list<Ts...>> {
static constexpr size_t value = sizeof...(Ts);
};
static_assert(size<type_list<int, char, double>>::value == 3);
实际应用中,std::tuple、std::variant、工厂函数、日志宏等都深度依赖可变参数模板。
基本上就这些。掌握参数包声明、sizeof...、递归分解和C++17折叠表达式,就能应对绝大多数场景。









