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

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

C++11 标准通过 Lambda 表达式、可变参数模板及包装器三大特性显著提升了代码简洁性与灵活性。Lambda 解决了局部逻辑定义的繁琐问题,可变参数模板打破了参数数量限制,function 与 bind 则实现了可调用对象的统一管理。深入解析了 Lambda 捕获机制、参数包展开策略以及函数绑定原理,结合具体代码演示了如何在实际开发中应用这些特性,并通过典型习题巩固对范围 for 循环及容器适配器的理解。

岁月神偷发布于 2026/3/23更新于 2026/10/2284 浏览
C++11 新特性详解:Lambda 表达式、可变参数模板与包装器
前言

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

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

文中不仅会拆解易混淆的概念(如 Lambda 的 mutable 修饰符作用、placeholders 占位符的使用规则),还会结合具体代码示例(如用 vector 存储 function 对象、用 bind 适配类成员函数),帮助读者理解这些特性的设计逻辑与实际价值。同时,文末的作业解析也将围绕 C++11 常见考点(如范围 for 的适用场景)展开,进一步巩固对标准特性的理解,为后续高效使用 C++11 进行开发打下基础。

Lambda 表达式

比如:一个类里面有很多成员变量,想对他们都进行排序,但是每次比较的逻辑不一样,而且还要每次都写一个类去比较,太麻烦了,所以就引入了 lambda 表达式。

Lambda 表达式是局部的匿名的函数对象,所以 sort 填仿函数那里可以写这个。

底层其实是用的仿函数实现的。

这个表达式不存在重载的说法。引申一下:auto f1 = []{}; 和 auto f2 = []{};,f1 和 f2 也不是同一类型,f1 = f2 的话会报错。也就是说,Lambda 表达式之间不能相互赋值,即使看起来类型相同。

其格式如下:

[捕捉列表](参数列表)(mutable)-> 返回值类型 { 函数体 };

捕获列表可以捕获表达式所在域的局部变量,函数也可以捕捉,全局域里的不用捕获也能用。强行捕获可能会报错。参数列表如果不传参,括号可以省略。mutable 修饰符可以取消捕获列表里东西默认的 const 性,但在使用该修饰符时,参数列表不能省略。如果捕获的东西本来是 const 的,加了 mutable 也变不了。注意:捕获列表默认是 const 的,返回值类型一般可以不用写,编译器会推导。

关于捕获列表(没用 mutable 的话,捕获的东西都不能被修改):

  • [var]:表示值传递方式捕捉变量 var,var 的类型会跟传递过来的一模一样。
  • [=]:表示值传递方式捕获所有父作用域中的变量(包括 this)。
  • [&var]:表示引用传递捕捉变量 var。
  • [&]:表示引用传递捕捉所有父作用域中的变量(包括 this)。

这里面的 & 和 =:如果是 [&, x] 那就只有 x 是值传递,其他都是引用。注意:捕捉列表不允许变量重复传递,否则就会导致编译错误,例如 [=, a] 就不行,a 捕获了两次。

用法展示:

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

可变参数模板

就是让模板参数可以是不确定的数目。其实日常自己写的话很少用的。

template<class... Args>
void ShowList(Args... args) {}

Args 表示一个包含零个或多个类型的参数包, 也是参数包,里面可以是 0 或多个参数。 是展开参数包的操作(放在参数包后面,除了模板参数声明那里)。但是这个参数包想知道里面的参数是啥的话比较困难,不支持 这样。

args
...
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 有可变参数模板,push_back 没有。emplace_back 可以直接传零散的参数进去,但是 push_back 必须要先把零散的参数搞成临时对象再传进去。

包装器
Function 包装器

Function 包装器也叫作适配器,需要头文件 #include <functional>。

作用:并且包装器可以让可调用对象存储到容器里面去了。

用法:Function 的类模板原型:

template<class T> function; // 这是模板的声明
template<class Ret, class... Args>
class function<Ret(Args...)>;

Ret:被调用函数的返回类型。 Args...:被调用函数的形参。

使用:

double f(double i) { return i / 2; }
struct Functor {
    double operator()(double d) { return d / 3; }
};
vector<function<double(double)>> v = { f, [](double d) -> double { return d / 4; }, Functor() };

第一个 double 是返回值类型,第二个 double 是形参类型。两个 double 类型的形参的话就写 (double, double)。

用法还有:

map<string, function<int(int)>> map1 = {
    {"a", [](int x) { return x; }},
    {"y", [](int x) { return x + 1; }}
};
// 用的话就:
int x = 0;
int a = map1["a"](x); // 注意理解 map1["a"] 取出包装器.....
Bind 函数

这个的头文件也是 #include <functional>。bind 是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象,生成一个新的可调用对象来'适应'原对象的参数列表。

这个的作用也就是把参数的顺序改了,还可以固定一些参数的值。

这里固定参数的值跟直接给缺省值的比较:缺省值只能给固定的参数一个固定值,这里就不一样了。

用法:对于普通的函数:

double Plus(int a, int b, double rate) { return (a + b) * rate; }
function<double(int, int)> Plus1 = bind(Plus, placeholders::_2, placeholders::_1, 4.0);

这个的话就是 rate 固定是 4.0,然后 Plus1(9, 8)。这里的 placeholders::_1 表示的是 (9, 8) 里面的 9,然后会传给 b。如果想固定的值在中间的话:

double Plus(int a, double rate, int b) { return (a + b) * rate; }
function<double(int, int)> Plus1 = bind(Plus, placeholders::_2, 4.0, placeholders::_1);
Plus1(9, 8); // 8 最终给了 a

这个依旧是 _1, _2 这样的哈。

用法:对于类里面的成员函数的话:

如果函数是静态的,那到没啥区别(但是记得加上类域)。如果函数不是静态的:函数后面要跟上这个对象或者对象的指针才行。函数是静态的话,可以加那个也可以不加。

class SubType {
public:
    int sub(int a, int b) { return a - b; }
};
SubType st;
function<double(int, int)> Sub2 = bind(&SubType::sub, &st, placeholders::_1, placeholders::_2, 3);
function<double(int, int)> Sub2 = bind(&SubType::sub, st, placeholders::_1, placeholders::_2, 3);
function<double(int, int)> Sub3 = bind(&SubType::sub, SubType(), placeholders::_1, placeholders::_2, 3); // 这里用的是匿名对象

引申:类里面的函数在外面用的时候必须要到类域里面去找才行。

作业部分

下面关于范围 for 说法错误的是(C)

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

说明:上面说的是容器,但是 stack 是容器适配器。

目录

  1. 前言
  2. Lambda 表达式
  3. 可变参数模板
  4. 展开参数包的方法
  5. 应用
  6. 包装器
  7. Function 包装器
  8. Bind 函数
  9. 作业部分

更多推荐文章

查看全部
  • 使用 Anthropic Skill 提升大模型前端设计质量
  • Test-Agent:开源软件测试智能助手
  • 使用 ISAAC Lab 训练机器人行走
  • 基于 OpenCASCADE 的分层截面几何体重建与 STL 导出
  • Harness Engineering:AI Agent 时代软件工程的范式转变
  • Java 8 (JDK 1.8) 下载、安装及环境变量配置教程
  • 飞算 JavaAI 插件使用体验与功能分析
  • 算法题:接雨水问题详解与动态规划解法
  • 大型语言模型结构分类详解
  • 360 大模型半年创收 2000 万,成国内首家实现商业化厂商
  • 大模型时代企业转型:RAG 技术的演进与实践挑战
  • C++ 异常处理机制:异常捕获、自定义异常与实战应用
  • 大模型学习进阶:五个阶段练级指南
  • AI 大模型原理、应用与未来趋势
  • Go 语言信用卡号码 Luhn 算法校验实现
  • RTX50 系显卡与 CUDA、PyTorch、Python 版本对应关系整理
  • OpenClaw 联网工具配置指南:提升 AI 实时信息获取能力
  • Python 学习资源推荐:Real Python 平台使用指南
  • Linux 序列化与反序列化原理及自定义网络协议实现
  • 商汤开源 SenseNova-MARS:多模态搜索推理模型突破

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如AES、TripleDES、Rabbit或RC4)加密和解密文本明文。 在线工具,加密/解密文本在线工具,online

  • Gemini 图片去水印

    基于开源反向 Alpha 混合算法去除 Gemini/Nano Banana 图片水印,支持批量处理与下载。 在线工具,Gemini 图片去水印在线工具,online

  • 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