概述
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] 这样的访问。
参数包展开方式
主要有两种展开方式:
- 递归展开参数包。
- 逗号表达式展开参数包。
应用场景
假设有个日期类 Date,我们可以这样写:
template<... Args>
Date* {
Date* ret = (args...);
ret;
}



