lambda表达式、std::function与std::bind

lambda表达式

lambda表达式是C++11引入的特性,允许定义匿名函数,简化参数的传递

1
auto func = [capture](params) opt -> ret { func_body; }
  • capture 捕获列表
    用于捕获lambda表达式所处的作用域中的外部变量
    • [] 不捕获任何变量
    • [] 按引用所有外部捕获变量
    • [=] 按值捕获所有外部变量
    • [=, &a] 按值捕获所有外部变量,同时按引用捕获变量a
    • [a] 按值捕获变量a
    • [this] 捕获当前对象的this指针
  • params 参数列表
  • opt 函数选项,例如mutable(可以修改按值捕获的变量)
  • ret 返回类型

std::function

可调用对象

可调用对象是指可以被调用的实体,只要满足以下条件之一即可称为调用对象

  • 函数指针
  • 具有operator()成员函数的类对象
  • 可以被转换为函数指针的类对象
  • 类成员函数指针
  • lambda表达式
  • std::bind表达式或其他函数对象

std::function

std::function是一个通用的函数封装器,可以存储、复制和调用任何可调用对象。其模板参数用于指定函数的签名,例如

1
std::function<void(int)> f;

中的模板参数void(int)表示接受一个参数为int且返回void的可调用对象。

示例

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <functional>
#include <iostream>

struct Foo {​
Foo(int num) : num_(num) {}​
void print_add(int i) const { std::cout << num_ + i << '\n'; }​
int num_;​
};​

void print_num(int i) { std::cout << i << '\n'; }​

struct PrintNum {​
void operator()(int i) const { std::cout << i << '\n'; }​
};​

int main() {​
// 存储自由函数​
std::function<void(int)> f_display = print_num;​
f_display(-9);​

// 存储 lambda 表达式​
std::function<void()> f_display_42 = []() { print_num(42); };​
f_display_42();​

// 存储 std::bind 的结果​
std::function<void()> f_display_31337 = std::bind(print_num, 31337);​
f_display_31337();​

// 存储成员函数​
std::function<void(const Foo&, int)> f_add_display = &Foo::print_add;​
const Foo foo(314159);​
f_add_display(foo, 1);​

// 存储数据成员访问器​
std::function<int(Foo const&)> f_num = &Foo::num_;​
std::cout << "num_: " << f_num(foo) << '\n';​
}​
  • std::function如果为空,在调用时会抛出std::bad_function_call异常
  • std::function常用语回调函数和事件处理等场景

std::bind

std::bind 用于将可调用对象与其参数绑定,生成一个新的可调用对象。它的主要作用有:​

  • 将可调用对象与参数绑定为另一个 std::function。​
  • n 元可调用对象转换为 m 元可调用对象(m < n),即部分应用。
    示例
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
#include <functional>
#include <iostream>
#include <memory>

void f(int n1, int n2, int n3, const int& n4, int n5) {​
std::cout << n1 << ' ' << n2 << ' ' << n3 << ' ' << n4 << ' ' << n5 << std::endl;​
}​

int g(int n1) { return n1; }​

struct Foo {​
void print_sum(int n1, int n2) { std::cout << n1 + n2 << std::endl; }​
int data = 10;​
};​

int main() {​
using namespace std::placeholders; // 引入占位符 _1, _2, _3...​

// 参数重排序和按引用传递​
int n = 7;​
auto f1 = std::bind(f, _2, 42, _1, std::cref(n), n);​
n = 10;​
f1(1, 2, 1001); // 调用 f(2, 42, 1, n, 7)​

// 嵌套 bind​
auto f2 = std::bind(f, _3, std::bind(g, _3), _3, 4, 5);​
f2(10, 11, 12); // 调用 f(12, g(12), 12, 4, 5)​

// 绑定成员函数​
Foo foo;​
auto f3 = std::bind(&Foo::print_sum, &foo, 95, _1);​
f3(5);​

// 绑定数据成员​
auto f4 = std::bind(&Foo::data, _1);​
std::cout << f4(foo) << std::endl;​
}
  • 使用占位符(如 _1_2)可以灵活地重排序参数。​
    例如_2就表示这是绑定后的可调用对象的第2个参数
  • std::bind 常用于延迟调用和参数部分应用。

std::bindlambda表达式

实际上,std::bind能做的,lambda都能做,并且更加优雅。
例如

  1. 绑定参数

这是最常见的用法。std::bind 将一个或多个参数预先绑定到一个函数上。

  • std::bind 写法:

    1
    2
    3
    4
    5
    void print_sum(int a, int b) {
    std::cout << a + b << std::endl;
    }
    auto print_10_plus_5 = std::bind(print_sum, 10, 5);
    print_10_plus_5(); // 输出 15
  • lambda 等价写法:

    1
    2
    auto print_10_plus_5_lambda = [](){ print_sum(10, 5); };
    print_10_plus_5_lambda(); // 输出 15
  1. 使用占位符 (placeholders)

std::bind 使用占位符(如 _1, _2)来指定调用时提供的参数应该放在哪个位置,甚至可以重排参数顺序。

  • std::bind 写法:

    1
    2
    3
    4
    5
    6
    7
    using namespace std::placeholders; // for _1, _2
    void print_ordered(int a, int b, int c) {
    std::cout << a << ", " << b << ", " << c << std::endl;
    }
    // 创建一个函数,它接受两个参数,并把它们作为第一和第三个参数传给 print_ordered,同时第二个参数固定为 100
    auto f_bind = std::bind(print_ordered, _1, 100, _2);
    f_bind(1, 99); // 输出 1, 100, 99
  • lambda 等价写法:
    lambda 通过自己的参数列表来自然地实现这一点,而且可读性更高。

    1
    2
    auto f_lambda = [](int x, int z){ print_ordered(x, 100, z); };
    f_lambda(1, 99); // 输出 1, 100, 99
  1. 绑定成员函数

std::bind 可以用来绑定类的成员函数。

  • std::bind 写法:

    1
    2
    3
    4
    5
    6
    struct MyClass {
    void member_func(int val) { std::cout << "Value: " << val << std::endl; }
    };
    MyClass instance;
    auto bound_member = std::bind(&MyClass::member_func, &instance, _1);
    bound_member(42); // 输出 "Value: 42"
  • lambda 等价写法:
    通过捕获实例指针或引用,lambda 可以更直观地实现。

    1
    2
    3
    MyClass instance;
    auto lambda_member = [&instance](int val){ instance.member_func(val); };
    lambda_member(42); // 输出 "Value: 42"

lambda表达式能做的std::bind却不一定能做,例如

  • 在原函数基础上添加额外逻辑:lambda 的函数体可以包含任意复杂的代码,而 std::bind 只能进行函数调用。

  • 泛型编程:C++14 引入了泛型 lambda,参数类型可以用 auto,这是 std::bind 无法做到的。

    1
    auto generic_lambda = [](auto x, auto y) { return x + y; };
  • 此外,现代编译器对 lambda 的优化非常出色,通常能生成比 std::bind 更快的代码。


lambda表达式、std::function与std::bind
https://guts.homes/2025/06/22/lambda/
作者
guts
发布于
2025年6月22日
许可协议