C++ -- 可变参数模板

template<typename F, typename... Args> 是 C++11 引入的‌**可变参数模板(Variadic Templates)**‌声明。

它是现代 C++ 泛型编程的基石,允许函数或类接受‌任意数量 ‌、‌任意类型 ‌的参数。这行代码通常出现在实现通用工具(如 std::bind, std::make_shared, std::thread 构造函数等)的场景中。

1. 语法拆解

  • ‌**typename F** ‌:
    • 定义了一个普通的模板类型参数 F
    • 通常用来表示‌可调用对象 ‌的类型(如函数指针、Lambda、仿函数、std::function 等)。
  • ‌**typename... Args** ‌:
    • 定义了一个‌**模板参数包(Template Parameter Pack)**‌。
    • ...(省略号)是关键,它表示 Args 不是一个单一类型,而是一系列类型的集合(例如 int, double, std::string)。
    • 这个包可以包含 0 个、1 个或多个类型。

2. 核心用法:如何"解开"参数包?

定义了参数包后,你不能直接像使用普通类型那样使用 Args。你需要通过‌包展开(Pack Expansion) ‌来操作它。最常见的场景是配合‌**完美转发(Perfect Forwarding)**‌。

典型示例:实现一个简单的 invoke 函数
cpp 复制代码
#include <iostream>
#include <utility> // for std::forward

// 1. 声明可变参数模板
template<typename F, typename... Args>
auto my_invoke(F&& f, Args&&... args) -> decltype(auto) 
{
    // 2. 包展开:将 args 包展开,并对每个参数应用 std::forward
    //    std::forward<Args>(args)... 
    //    如果 Args 是 <int, double>,args 是 (a, b)
    //    展开后变成:std::forward<int>(a), std::forward<double>(b)
    
    return std::forward<F>(f)(std::forward<Args>(args)...);
}

void print_sum(int a, int b) {
    std::cout << "Sum: " << a + b << std::endl;
}

int main() {
    // 调用普通函数
    my_invoke(print_sum, 10, 20); 

    // 调用 Lambda
    my_invoke([](const std::string& s) { 
        std::cout << "Hello, " << s << std::endl; 
    }, std::string("World"));

    return 0;
}

3. 关键概念解析

A. 万能引用(Universal Reference / Forwarding Reference)

注意参数列表中的 F&&Args&&...

  • 在模板中,T&& 并不一定代表右值引用。
  • 如果传入的是左值,T 会被推导为 T&,根据引用折叠规则,T& && 变成 T&(左值引用)。
  • 如果传入的是右值,T 被推导为 TT&& 保持为右值引用。
  • 目的 ‌:这使得 my_invoke 能够接收任何类型的参数(左值、右值、const、非 const),并保留其原始属性。
B. std::forward 的作用
  • std::forward<Args>(args)... 确保参数在传递给内部函数 f 时,保持其原始的左值/右值属性。
  • 如果没有 std::forward,所有参数在函数内部都会变成左值,可能导致不必要的拷贝(例如无法触发移动语义)。
C. decltype(auto) (C++14)
  • 用于自动推导返回类型。
  • 它不仅能推导类型,还能保留引用限定符。如果 f 返回一个引用,my_invoke 也返回引用;如果返回值,它也返回值。

4. 常见应用场景

1‌、std::thread 的构造函数‌:

cpp 复制代码
// 允许你传递任意函数和任意参数给新线程
std::thread t(my_function, arg1, arg2, arg3);

2、**std::make_shared / std::make_unique**‌

cpp 复制代码
// 允许你在创建智能指针的同时,直接传递构造函数的参数
auto p = std::make_shared<MyClass>(1, "hello", 3.14);

‌3、日志系统‌:

cpp 复制代码
template<typename... Args>
void log(const char* fmt, Args... args) {
    printf(fmt, args...); // 展开参数包传给 printf
}

5. 总结

  • ‌**typename F**‌:捕获可调用对象。
  • ‌**typename... Args**‌:捕获任意数量的参数类型。
  • ‌**Args&&... args**‌:通过万能引用接收任意类型的参数值。
  • ‌**std::forward<Args>(args)...**‌:完美转发这些参数,保持其左值/右值属性,避免多余拷贝。

这是编写‌高性能、通用、类型安全‌的 C++ 库代码的标准模式。

相关推荐
蒙奇·D·路飞-1 小时前
OpenClaw:开源的《合金弹头》精神续作与终极本地部署指南
c++·开源·mfc
小星星闪亮登场3 小时前
2026萌新联赛第三场-- (郑州轻工业大学)
数据结构·c++·经验分享·算法·贪心算法·排序算法·深度优先
梓䈑3 小时前
【用 Vibe Coding 实现的 C++17 在线判题系统】前端开发 + Web 自动化测试
前端·c++·ai编程
冻柠檬飞冰走茶3 小时前
PTA基础编程题目集 7-8超速判断(C++语言实现)
开发语言·数据结构·c++·算法
TunerT_TQ4 小时前
Valhalla 静态工程审阅 #018|MMCV 源码证据驱动评测【大厂开源基础设施特辑】
c++·开源·mfc·#计算机视觉·#商汤科技·#openmmlab·#静态源码审计
别动我齐刘海5 小时前
Day6 unitree_G1人形机器人GMR—— MotionInput
c语言·c++·人工智能·学习·机器学习·机器人·github
Henry Zhu1236 小时前
C++17 `weak_from_this` 详解:让对象安全地观察自己
c++
June`9 小时前
warp shuffle指令
c++·人工智能·算法·cuda
乱七八糟的屋子9 小时前
【C++数值计算】NumCpp超详细入门教程(纯正Numpy语法、零学习成本、C++首选科学计算库、性能压测对比)
c++·数值计算·numpy·高性能计算·科学计算·ndarray
choumin9 小时前
C程序能调用extern “C“声明的带默认参数的C++函数吗?
c语言·c++·extern c·默认参数