# 可变参数函数模板 Variable templates 允许模板接受任意数量和任意类型的参数。 1.基本用法 ```c++ template void func(Args... args); ``` 使用...操作符展开参数包 ```c++ template void print(Args... args) { (std::cout << ... << args) << std::endl; // C++17 折叠表达式 } ``` 2.基本使用 ```c++ #include //递归终止 void print() { std::cout << std::endl; } //可变参数模板 template void print(T first, Args... args) { std::cout << first << " "; print(args...); //递归调用,参数减一 } //use print(1, 2.5, "hello"); ``` 折叠表达式 ```c++ template void print_fold(Args... args) { //一元右折叠 ((std::cout << args << " "), ...); std::cout << std::endl; //sum int sum = (args + ...); // (args1 + args2 + args3 + ...) std::cout << "Sum: " << sum << std::endl; } //use print_fold(1, 2, 3, 4); //Output: 1 2 3 4 Sum: 10 ``` 计算参数数量 ```c++ template constexpr size_t count_args(Args... args) { return sizeof...(Args); // 或 sizeof...(args) } // 使用 std::cout << count_args(1, 2.5, "test") << std::endl; // 输出: 3 ``` 完美转发 ```c++ template std::unique_ptr make_unique(Args&&... args) { return std::unique_ptr(new T(std::forward(args)...)); } // 使用 auto ptr = make_unique>(5, 42); // 创建包含5个42的vector ``` 构造函数转发 ```c++ class Container { template void emplace_back(Args&&... args) { data_.emplace_back(std::forward(args)...); } private: std::vector data_; }; ``` 类型安全的格式化 ```c++ #include #include template std::string format(const std::string& fmt, Args... args) { std::ostringstream oss; // 简化实现 - 实际中需要解析格式字符串 size_t index = 0; auto process = [&](const auto& arg) { std::string placeholder = "{" + std::to_string(index++) + "}"; // 这里需要更复杂的格式化逻辑 oss << arg << " "; }; (process(args), ...); // C++17 折叠表达式 return oss.str(); } ``` --- # 变参模板 + 完美转发 C++ 变参模板 + 完美转发是 C++ 现代开发中最核心、最高效的组合技术: - **变参模板:处理任意个数、任意类型的参数** - **完美转发:保留参数的值类别(左值 / 右值),不产生额外拷贝,零开销转发** 两者配合,能实现**通用零拷贝、通用型工厂函数 / 包装器**,是 STL 容器、智能指针、异步任务的底层实现原理。 ## **一、核心概念速览** ### 1. 变参模板 (Variadic Templates) 语法:`template`,表示**一包类型参数**调用时可以传入 0 个、1 个、N 个任意类型参数。 ### 2. 完美转发 (Perfect Forwarding) 核心工具: - 转发引用:T&&(模板中才是转发引用) - `std::forward(arg):**精确保留参数的左值 / 右值属性**` 作用:让函数像 “透明管道” 一样转发参数,**不修改、不拷贝、不丢失属性**。 ## **二、标准组合写法(固定模板)** ```cpp //万能转发模板 #include // std::forward // 变参模板 + 完美转发 标准写法 template void forward_func(Args&&... args) { // 转发引用 + 变参 // 完美转发所有参数给目标函数 target_func(std::forward(args)...); } ``` 关键语法解释 1. `Args&&... args`:变参转发引用,接受任意参数。 2. `std::forward(args)...` :展开并完美转发所有参数。 3. `...` 是参数包展开符号,必须放在后面。 ## **三、最经典应用:通用工厂函数** 这是最实用的场景:用变参 + 完美转发,零拷贝构造任意对象。 1. 首先这是一个模板函数; 2. 该模板函数创建并返回一个对象; 3. 直接在目标内存构造,无任何拷贝/移动; ```cpp // 测试用类 class MyClass { public: MyClass(int a, double b, const std::string& c) { std::cout << "构造:" << a << " " << b << " " << c << "\n"; } MyClass(const MyClass&) = delete; // 禁用拷贝,验证无拷贝 MyClass(MyClass&&) = default; // 必须显式声明移动构造! // 原因:用户声明了拷贝构造(即使是=delete),编译器不会隐式生成移动构造函数。 // 按值返回对象时,需要移动构造函数可用(即使RVO省略了实际调用,它也必须存在)。 }; // ✅ 变参 + 完美转发:通用工厂(零拷贝构造对象) template T make_obj(Args&&... args) { // 直接在目标内存构造,无任何拷贝/移动 return T(std::forward(args)...); } int main() { std::cout << "\n--- 示例1:通用工厂函数 ---\n"; // 传入任意参数,直接构造 MyClass auto obj = make_obj(10, 3.14, "hello"); return 0; } ``` 优势 - 支持任意类型、任意个数的构造参数 - 完美转发,不产生拷贝、不产生移动 - 这就是 `std::make_unique`/`std::make_shared` 的底层实现 ## **四、第二大应用:包装函数(日志 / 钩子 / 通用代理)** 给任意函数加一层包装(如日志、计时、权限检查),不修改原函数、不丢失参数属性。 ```cpp #include #include #include // 目标函数(可以是任意函数) void print(int a, const std::string& b) { std::cout << a << " " << b << "\n"; } // 通用包装器:变参+完美转发 template void wrapper(Func&& func, Args&&... args) { std::cout << "[包装层] 开始调用\n"; // 完美转发函数 + 参数 std::forward(func)(std::forward(args)...); std::cout << "[包装层] 调用结束\n"; } int main() { // 包装任意函数 + 任意参数 wrapper(print, 100, "test"); return 0; } ``` ### 优势 - 一个包装器适配**所有函数签名** - 完美转发:左值保持左值,右值保持右值 - 无性能损耗 ## **五、第三大应用:STL 容器 emplace_back** vector::emplace_back 就是变参 + 完美转发的标准产物: ```cpp #include #include struct Student { int id; std::string name; Student(int i, std::string n) : id(i), name(std::move(n)) {} }; int main() { std::vector vec; // ✅ 变参+完美转发:直接在 vector 内存里构造,无拷贝 vec.emplace_back(1, "Tom"); return 0; } ``` `emplace_back 比 ``push_back` 高效的核心原因:**变参 + 完美转发,直接原地构造**。 ## **六、必须掌握的规则(避坑)** ### 1. 转发引用必须写在模板里 ```cpp // ✅ 正确:模板中的 T&& 是转发引用 templatevoid func(T&& t) {} // ❌ 错误:普通函数的 T&& 是右值引用,只能接收右值 void func(int&& t) {} ``` 2. 必须用 std::forward 才叫完美转发 ```cpp // ❌ 错误:丢失右值属性,全部转为左值,可能产生拷贝 target_func(args...); // ✅ 正确:完美保留左值/右值属性 target_func(std::forward(args)...); ``` 3. 参数包展开 `...` 位置不能错 ```cpp // ✅ 正确 std::forward(args)... // ❌ 错误 std::forward(args...) ``` ## **七、总结(核心要点)** 1.组合目的:处理*任意参数* + *零拷贝转发*,是现代 C++ 高性能基石 2.固定写法 ```cpp template void func(Args&&... args) { target(std::forward(args)...); } ``` 3.三大应用 - 通用工厂函数(`make_shared`) - 通用包装器(日志 / 代理 / 钩子) - 容器原地构造(`emplace_back`) 4.核心价值:通用、灵活、*零运行时开销*