C 语言

关注公众号 jb51net

关闭
首页 > 软件编程 > C 语言 > C++ std::function 与 std::bind

C++11  Lambda 表达式、std::function 与 std::bind 解析

作者:handler01

这段文章详细解析了C++1Lambda表达式、std::function和std::bind的核心概念、语法结构与应用场景,特别强调了Lambda表达式在代码紧凑性和可读性上的优势,以及std::function和std::bind在解耦和类型擦除中的重要性

一、 Lambda 表达式

1.1 核心概念与语法

组成部分是否必填说明
[capture-list]必填捕捉列表:控制 Lambda 访问外部局部变量的方式。
(parameters)可选参数列表:与普通函数一致,无参可省略 ()
mutable可选默认按值捕捉的变量副本是 const 的,添加此关键字后可修改副本。
-> return-type可选返回值类型:通常可由编译器自动推导,复杂逻辑下可显式指定。
{ body }必填函数体:实现具体逻辑。

❓ 发散:为什么 Lambda 表达式默认按值捕捉的变量是不可修改的(需要加 mutable)?

💡 解答:因为 Lambda 底层会被编译器转换为一个仿函数类。按值捕捉的变量会变成该类的成员变量。默认情况下,编译器生成的 operator() 是一个 const 成员函数,因此无法修改类的成员变量。加上 mutable 关键字,本质上就是去掉了 operator()const 限定符。

二、 std::function (多态函数包装器)

2.1 核心概念与底层机制

2.2 核心价值与应用场景

2.3 复杂业务逻辑的进阶处理

笔记

当业务接口复杂,或需要额外参数(如数据库句柄等)时,有三种标准做法将其适配为 std::function 要求的签名:

方法一:利用 Lambda 表达式“捕获”变量(最现代、简洁)

Database db; 
server.Start([&db](std::shared_ptr<Socket> &sock, InetAddr &client) {
    db.Query(sock->Recv(...)); // 捕获外部 db 进行操作
});

方法二:使用 std::bind 进行参数适配

void SuperService(std::shared_ptr<Socket>& s, InetAddr& a, MyManager* mgr);
MyManager manager;
// 将 mgr 压入,生成只接受 2 个参数的新对象
auto service = std::bind(SuperService, std::placeholders::_1, std::placeholders::_2, &manager);
server.Start(service); 

方法三:封装成“业务类”(仿函数)

class ChatBusiness {
public:
    void operator()(std::shared_ptr<Socket> &sock, InetAddr &client) {
        Login(); SaveMsg(); // 执行内部复杂逻辑
    }
private:
    void Login(); void SaveMsg();
};
ChatBusiness cb;
server.Start(cb);

一句话总结std::function 让服务器框架不再关心“做什么”,而只关心“怎么连”,实现了一套代码、百种业务的高度复用(扮演“分拣员”角色)。

三、 std::bind (函数参数绑定)

3.1 核心概念与占位符

#include <functional>
template< class F, class... Args >
/* 返回值类型未命名,通常用 auto 或 std::function 接收 */
bind( F&& f, Args&&... args );

功能与参数说明f 是待包装的可调用对象;args 是要绑定的参数列表,可以是具体数值(提前固定),也可以是占位符。返回值是一个临时的仿函数对象。

3.2 基础与进阶用法

// 新函数的参数 2 传给 a,参数 1 传给 b
auto f1 = std::bind(Sub, std::placeholders::_2, std::placeholders::_1); 
// f1(10, 5) 实际执行 Sub(5, 10),结果为 -5

用法 2:调整个数(固定参数 / 局部应用)

// a 强制固定为 100,新函数的参数 1 传给 b
auto f2 = std::bind(Sub, 100, std::placeholders::_1);
// f2(5) 实际执行 Sub(100, 5),结果为 95

用法 3:绑定类成员函数(高频场景)

class Plus {
public:
    int plusd(int a, int b) { return a + b; }
};
Plus pd;
auto f = std::bind(&Plus::plusd, &pd, std::placeholders::_1, std::placeholders::_2); 
// f(10, 20) 等价于 pd.plusd(10, 20)

❓ 导师发散提问:为什么大部分情况下,现代 C++ 提倡用 Lambda 替代 std::bind

💡 解答:首先是可读性,嵌套的 std::bind 和大量的 _1, _2 占位符极难阅读,而 Lambda 参数流向清晰;其次是性能std::bind 内部机制复杂且依赖大量模板元编程,其生成的对象很难被编译器彻底内联优化,而 Lambda 会生成简单的局部匿名类,编译器可以轻易将其内联展开。

到此这篇关于C++11 Lambda 表达式、std::function 与 std::bind 解析的文章就介绍到这了,更多相关C++11 std::function 与 std::bind内容请搜索脚本之家以前的文章或继续浏览下面的相关文章希望大家以后多多支持脚本之家!

您可能感兴趣的文章:
阅读全文