可变参数模板
内容简介
可变参数模板允许模板接收任意数量的模板参数或函数参数。它是 std::make_unique、std::make_shared、完美转发、格式化库、通用工厂函数的基础。
核心概念
template <typename... Args>
void func(Args... args)
{
}typename... Args:模板参数包。Args... args:函数参数包。args...:展开参数包。
基础用法
打印任意数量参数:
template <typename T>
void print_one(const T& value)
{
std::cout << value << '\n';
}
template <typename... Args>
void print_all(const Args&... args)
{
(print_one(args), ...); // C++17 折叠表达式
}
int main()
{
print_all(1, "hello", 3.14);
}递归展开写法
C++17 前常用递归展开:
void print_all()
{
}
template <typename T, typename... Rest>
void print_all(const T& first, const Rest&... rest)
{
std::cout << first << '\n';
print_all(rest...);
}折叠表达式
C++17 提供折叠表达式,写起来更直接:
template <typename... Args>
auto sum(Args... args)
{
return (args + ...);
}带初始值:
template <typename... Args>
auto sum(Args... args)
{
return (0 + ... + args);
}和完美转发结合
通用工厂函数:
template <typename T, typename... Args>
std::unique_ptr<T> make_object(Args&&... args)
{
return std::make_unique<T>(std::forward<Args>(args)...);
}这里的重点:
Args&&...是转发引用参数包。std::forward<Args>(args)...保留每个实参的左值/右值属性。- 这类写法和 完美转发 强相关。
最佳代码实践
- 简单参数包展开优先用 C++17 折叠表达式。
- 需要保留参数值类别时,使用
Args&&...和std::forward<Args>(args)...。 - 参数包函数要保持语义清楚,不要写成看不懂的模板魔法。
- 如果所有参数要求同一类型,
std::initializer_list或容器可能更简单。
错误用法
template <typename... Args>
void save(Args&&... args)
{
use(args...); // 错误倾向:丢失右值属性
}更好的写法:
template <typename... Args>
void save(Args&&... args)
{
use(std::forward<Args>(args)...);
}