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

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

综述由AI生成C++11 引入了多项简化开发的核心特性。Lambda 表达式允许定义匿名函数对象,解决局部逻辑复用问题;可变参数模板支持不定数量参数的泛型编程;function 包装器与 bind 适配器统一了可调用对象管理并支持参数绑定。结合实例解析语法细节、捕获规则及实际应用,并通过习题巩固范围 for 循环等知识点。

flc发布于 2026/3/15更新于 2026/6/916 浏览
C++11 新特性详解:Lambda、可变参数模板与包装器

概述

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

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

Lambda 表达式

在实际开发中,如果类中有多个成员变量需要排序,但每次比较逻辑不同,传统做法是编写多个仿函数类,非常繁琐。Lambda 表达式正是为了解决这个问题而引入的。它是局部的匿名函数对象,其本质是编译器生成的临时仿函数对象。

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

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

关于捕获列表,如果没有使用 mutable,捕获的东西都不能被修改:

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

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

值得注意的是,Lambda 表达式本身不支持重载。即使两个 Lambda 看起来类型相同,它们也是不同的类型,不能相互赋值。可调用对象的四种存储方法包括:函数指针、仿函数、Lambda 表达式,以及用包装器搞到容器里面。

可变参数模板

可变参数模板让模板参数可以是不确定的数目。虽然日常自己写的话很少用,但它确实是 C++11 的重要特性之一。

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

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

参数包展开方式

主要有两种展开方式:

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

应用场景

假设有个日期类 Date,我们可以这样写:

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

可以 Date* p1 = Create(2025, 9, 7); 这样来用,或者 Date d(2025, 9, 7); Date* p1 = Create(d); 也行。

在 STL 容器中,emplace_back 有可变参数模板支持,可以直接传零散的参数进去,而 push_back 没用,必须要先把零散的参数搞成临时对象再传进去。

Function 包装器

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

Function 用法

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 使用:

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 函数

Bind 也是一个函数模板,它就像一个函数包装器(适配器),接受一个可调用对象,生成一个新的可调用对象来适应原对象的参数列表。它的作用是把参数的顺序改了,并且可以固定一些参数的值。这与直接给缺省值的区别在于,缺省值只能给固定的参数一个固定值,而 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 编译器最终是将其转化为迭代器来进行处理的

范围 for 循环练习题

选项 C 是错误的,上面说的是容器,但是 stack 是容器适配器,不支持随机访问或迭代器遍历。

目录

  1. 概述
  2. Lambda 表达式
  3. 可变参数模板
  4. 参数包展开方式
  5. 应用场景
  6. Function 包装器
  7. Function 用法
  8. Bind 函数
  9. 实战练习
  • 💰 8折买阿里云服务器限时8折了解详情
  • Magick API 一键接入全球大模型注册送1000万token查看
  • 🤖 一键搭建Deepseek满血版了解详情
  • 一键打造专属AI 智能体了解详情
极客日志微信公众号二维码

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

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

更多推荐文章

查看全部
  • Excel 自定义 Copilot 函数实现指南
  • MySTL:C++ 标准库精简实现与源码解析
  • DeepSeek 与通义万相结合制作 AI 视频实战指南
  • 利用 Python 开发副业:数据抓取与变现实战指南
  • Android Binder 线程池机制详解
  • 程序员 LLM 学习指南:从入门到进阶的技术路线
  • C++11 核心新特性详解:初始化、声明与移动语义
  • 基于 RetinaFace 和 CurricularFace 的教育考勤自动化方案
  • 挖 SRC 时的信息收集方法与工具实践
  • 使用 Biopython 快速解析 FASTA 与 GenBank 基因数据
  • Windows 7 系统 Python 3.8+ 兼容安装指南
  • 前端大数据导出优化:解决 Chrome 内存崩溃的实战方案
  • C++ 基础:引用、inline 与 nullptr 用法解析
  • 突破内存瓶颈:llama.cpp 项目中 KV 缓存优化策略全解析
  • MySQL 表操作实战:创建、修改与删除全解析
  • Llama-Factory 微调中 Batch Size 的设置与性能调优
  • C++ 模拟实现二叉搜索树
  • AI 大模型详解:定义、原理与核心应用
  • 2025 年 9 月电子学会 Python 一级编程等级考试真题及解析
  • FPGA 门电路映射机制:从逻辑到硬件架构解析

相关免费在线工具

  • 加密/解密文本

    使用加密算法(如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