跳到主要内容
极客日志极客日志面向AI+效率的开发者社区
首页博客GitHub 精选镜像AI 生图工具UI配色美学隐私政策关于联系
搜索内容 / 工具 / 仓库 / 镜像...⌘K搜索
注册
博客列表
C++

C++11 新特性详解:Lambda 表达式、可变参数模板与包装器

C++11 标准引入了多项核心特性以简化代码编写并统一管理可调用对象。详细讲解了 Lambda 表达式的语法与捕获逻辑、可变参数模板的参数包展开方法及其在 STL 中的应用,同时介绍了 std::function 包装器与 std::bind 函数如何整合不同类型的可调用对象。内容涵盖语法规则、底层实现原理及实际开发场景,旨在帮助开发者深入理解并高效使用 C++11 特性。

AiEngineer发布于 2026/3/30更新于 2026/7/1240 浏览
C++11 新特性详解:Lambda 表达式、可变参数模板与包装器

前言

在 C++11 标准带来的诸多革命性特性中,简化代码编写与统一可调用对象管理是两大核心目标。Lambda 表达式解决了传统仿函数定义繁琐、复用性低的问题,让局部场景下的自定义逻辑能以更简洁的匿名函数形式实现;可变参数模板打破了模板参数数量固定的限制,为 STL 容器(如 emplace_back)和通用函数设计提供了灵活的参数处理能力;而 function 包装器与 bind 函数,则进一步整合了函数指针、仿函数、lambda 等不同类型的可调用对象,实现了统一管理与参数适配。

这些特性并非孤立存在。Lambda 的底层依赖仿函数实现,可变参数模板为 emplace 系列接口提供了技术支撑,function 与 bind 则基于前两者的特性,解决了不同可调用对象类型不统一的问题。本文将从实际开发需求出发,讲解 Lambda 表达式的语法规则与捕获逻辑,深入可变参数模板的展开方法与应用场景,并通过 function 包装器与 bind 函数的实例,展示如何统一管理各类可调用对象、灵活调整参数顺序与固定参数值。

文中将拆解易混淆的概念(如 Lambda 的 mutable 修饰符作用、placeholders 占位符的使用规则),并结合具体代码示例,帮助读者理解这些特性的设计逻辑与实际价值。

Lambda 表达式

Lambda 表达式是局部的匿名函数对象,其底层其实是用的仿函数实现的。它不存在重载的说法,即使两个 Lambda 看起来类型相同,它们也不是同一类型,不能相互赋值。

语法格式

[捕捉列表](参数列表)(mutable)-> 返回值类型 { 函数体 };
  • 捕捉列表:可以捕捉该表达式所在域的局部变量。全局域里的不用捕获也能用。如果强行捕获可能会报错。
  • 参数列表:如果不传参,() 可以省略。
  • mutable:可以取消捕获列表里面东西默认的 const 性。在使用该修饰符的时候,参数列表不能省略。如果捕获的东西本来是 const 的话,加了 mutable 也变不了。
  • 返回值类型:一般可以不用写,编译器会推导。

捕获列表规则

注意:如果没有使用 mutable,捕获的东西都不能被修改。

  • [var]:表示值传递方式捕捉变量 var,var 的类型会跟传递过来的一模一样。
  • [=]:表示值传递方式捕获所有父作用域中的变量(包括 this)。
  • [&var]:表示引用传递捕捉变量 var。
  • [&]:表示引用传递捕捉所有父作用域中的变量(包括 this)。
  • [&, x]:只有 x 是值传递,其他都是引用。
  • 注意:捕捉列表不允许变量重复传递,否则会导致编译错误(例如 [=, a] 不行,a 捕获了两次)。

用法示例

#include <iostream>
#include <vector>
#include <functional>

int main() {
    int x = 10;
    // 值捕获
    auto f1 = [x]() { return x; };
    // 引用捕获
    auto f2 = [&x]() { x++; };
    
    std::cout << f1() << std::endl;
    return 0;
}

可调用对象的四个存储方法:函数指针、仿函数、Lambda 表达式、用包装器搞到容器里面。

可变参数模板

可变参数模板让模板参数可以是不确定的数目。虽然日常自己写的话很少用,但在 STL 内部广泛使用。

基本语法

template<class... Args>
void ShowList(Args... args) {}
  • Args 表示一个包含零个或多个类型的参数包。
  • args 也是参数包,里面可以是 0 或多个参数。
  • ... 是展开参数包的操作(放在参数包后面,除了模板参数声明那里)。
  • 这个参数包想知道里面的参数是啥的话比较困难,不支持下标访问(eg: arg[1] 这样)。

展开参数包的方法

  1. 递归展开参数包。
  2. 逗号表达式展开参数包。

应用

构造函数转发

假设有个日期类 Date:

template<class... Args>
Date* Create(Args... args) {
    Date* ret = new Date(args...);
    return ret;
}

// 使用
Date* p1 = Create(2025, 9, 7);
// 或者
Date d(2025, 9, 7);
Date* p1 = Create(d);
emplace_back vs push_back
  • emplace_back 有可变参数模板支持,可以直接传零散的参数进去。
  • push_back 没用可变参数模板,必须要先把零散的参数搞成临时对象再传进去。

包装器

Function 包装器

Function 包装器也叫作适配器,需要头文件 #include <functional>。它可以让可调用对象存储到容器中去了。

类模板原型
template<class T> class function;

template<class Ret, class... Args>
class function<Ret(Args...)>;
  • Ret:被调用函数的返回类型。
  • Args...:被调用函数的形参。
使用示例
#include <functional>
#include <vector>
#include <map>
#include <string>

double f(double i) { return i / 2; }

struct Functor {
    double operator()(double d) { return d / 3; }
};

int main() {
    // vector 存储 function 对象
    std::vector<std::function<double(double)>> v = {
        f,
        [](double d) -> double { return d / 4; },
        Functor()
    };

    // map 存储 function 对象
    std::map<std::string, std::function<int(int)>> map1 = {
        {"a", [](int x) { return x; }},
        {"y", [](int x) { return x + 1; }}
    };

    int x = 0;
    int a = map1["a"](x);
    return 0;
}

Bind 函数

Bind 是一个函数模板,接受一个可调用对象,生成一个新的可调用对象来'适应'原对象的参数列表。它的作用是把参数的顺序改了,并且可以固定一些参数的值。

固定参数值

缺省值只能给固定的参数一个固定值,而 bind 可以更灵活地处理参数位置。

#include <functional>

double Plus(int a, int b, double rate) {
    return (a + b) * rate;
}

int main() {
    // rate 固定是 4.0
    std::function<double(int, int)> Plus1 = std::bind(
        Plus, 
        std::placeholders::_2, 
        std::placeholders::_1, 
        4.0
    );
    // Plus1(9, 8) -> Plus(8, 9, 4.0)
    return 0;
}

如果想固定的值在中间:

double Plus(int a, double rate, int b) {
    return (a + b) * rate;
}

std::function<double(int, int)> Plus1 = std::bind(
    Plus, 
    std::placeholders::_2, 
    4.0, 
    std::placeholders::_1
);
// Plus1(9, 8) -> 8 最终给了 a
类成员函数
  • 如果函数是静态的,那到没啥区别(但是记得加上类域)。
  • 如果函数不是静态的:函数后面要跟上这个对象或者对象的指针才行。
class SubType {
public:
    int sub(int a, int b) { return a - b; }
};

SubType st;
// 绑定非静态成员函数
std::function<int(int, int)> Sub2 = std::bind(
    &SubType::sub, 
    &st, 
    std::placeholders::_1, 
    std::placeholders::_2
);

// 也可以使用对象副本或匿名对象
std::function<int(int, int)> Sub3 = std::bind(
    &SubType::sub, 
    SubType(), 
    std::placeholders::_1, 
    std::placeholders::_2
);

作业解析

关于范围 for 循环的说法,以下错误的是(C)。

A. 范围 for 可以直接应用在数组上 B. 对于 STL 提供的所有容器,均可以使用范围 for 依次访问其元素 C. 使用范围 for 操作 stack,可以简化代码 D. 对于自定义类型,想要支持范围 for,必须提供 begin 和 end 迭代器 E. 范围 for 编译器最终是将其转化为迭代器来进行处理的

解析:选项 C 错误。上面说的是容器,但是 stack 是容器适配器,不支持直接的范围 for 遍历。

目录

  1. 前言
  2. Lambda 表达式
  3. 语法格式
  4. 捕获列表规则
  5. 用法示例
  6. 可变参数模板
  7. 基本语法
  8. 展开参数包的方法
  9. 应用
  10. 构造函数转发
  11. emplaceback vs pushback
  12. 包装器
  13. Function 包装器
  14. 类模板原型
  15. 使用示例
  16. Bind 函数
  17. 固定参数值
  18. 类成员函数
  19. 作业解析
  • 免费图片AI生成工具免费生成了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 免费图片视频在线生成30秒,将你的创意变成现实开始设计
  • X/Twitter免费视频下载器免登陆无限额度免费视频解析下载了解详情
  • 100+免费在线小游戏爽一把
极客日志微信公众号二维码

微信扫一扫,关注极客日志

微信公众号「极客日志V2」,在微信中扫描左侧二维码关注。展示文案:极客日志V2 zeeklog

更多推荐文章

查看全部
  • Stable Diffusion WebUI 无障碍改造:键盘导航与屏幕阅读器适配
  • YOLOv8与ROS结合构建机器人视觉感知系统
  • 基于 OpenCV 与 C++ 的 ISBN 图像识别系统实现
  • YOLO26:实时目标检测的关键架构改进与性能基准测试
  • Linux 环境下 OpenClaw 安装、初始化与 Web UI 配置指南
  • 前端与服务器时间同步方法及 Vue3 实现示例
  • 前端数据埋点:核心原理、主流方案与落地实践
  • Arrow 游戏叙事工具:三大实战场景与可视化创作
  • Docker Registry 私有仓库搭建实战 (v1/v2 详解)
  • Linux 命令实战:ar 归档工具使用详解
  • Mac M3 安装 Antigravity Agent 显示已损坏问题解决方案
  • 反射型 DLL 注入与柔性加载技术原理及实现
  • MySQL 事务与锁机制深度解析
  • Python 新手入门指南与学习路线
  • 本地部署 AI 量化分析平台:Docker 配置与波浪理论实战
  • 前端 EME DRM 反录屏原理与实战代码
  • 前端地图基本操作控制:平移、缩放、旋转及样式切换
  • Python 实现 MCP 客户端调用高德地图天气查询示例
  • Python Web UI 自动化测试:Jenkins 构建项目与定时执行
  • x86-64 内存架构与 mov 指令详解:寻址机制与栈操作深入剖析

相关免费在线工具

  • Base64 字符串编码/解码

    将字符串编码和解码为其 Base64 格式表示形式即可。 在线工具,Base64 字符串编码/解码在线工具,online

  • Base64 文件转换器

    将字符串、文件或图像转换为其 Base64 表示形式。 在线工具,Base64 文件转换器在线工具,online

  • Markdown转HTML

    将 Markdown(GFM)转为 HTML 片段,浏览器内 marked 解析;与 HTML转Markdown 互为补充。 在线工具,Markdown转HTML在线工具,online

  • HTML转Markdown

    将 HTML 片段转为 GitHub Flavored Markdown,支持标题、列表、链接、代码块与表格等;浏览器内处理,可链接预填。 在线工具,HTML转Markdown在线工具,online

  • JSON 压缩

    通过删除不必要的空白来缩小和压缩JSON。 在线工具,JSON 压缩在线工具,online

  • JSON美化和格式化

    将JSON字符串修饰为友好的可读格式。 在线工具,JSON美化和格式化在线工具,online