std::bind()和lambda的区别

0、背景

在查看muduo的开源代码的时候有一个代码:

cpp 复制代码
// 需要一个Timestamp的参数
void setReadCallback( std::function<void(Timestamp)> cb)

void EventLoop::handleRead();

//使用bind,只需要传递this,而没有传递Timestamp,也没有什么占位符标记
wakeupChannel_->setReadCallback(std::bind(&EventLoop::handleRead, this));

当我尝试使用lambda进行编写的时候:

cpp 复制代码
wakeupChannel_->setReadCallback([this]() { handleRead(); });

出现参数签名不匹配的错误:

csharp 复制代码
No viable conversion from '(lambda at 
/home/xvxing/workspace/cpp/mygame/src/net/EventLoop.cpp:35:37)' to 
'ReadEventCallback' (aka 'function<void (Timestamp)>')

[std_function.h(375, 7): ]()Candidate constructor not viable: no known conversion from '(lambda at /home/xvxing/workspace/cpp/mygame/src/net/EventLoop.cpp:35:37)' to 'nullptr_t' (aka 'std::nullptr_t') for 1st argument

[std_function.h(386, 7): ]()Candidate constructor not viable: no known conversion from '(lambda at /home/xvxing/workspace/cpp/mygame/src/net/EventLoop.cpp:35:37)' to 'const function<void (Timestamp)> &' for 1st argument

[std_function.h(404, 7): ]()Candidate constructor not viable: no known conversion from '(lambda at /home/xvxing/workspace/cpp/mygame/src/net/EventLoop.cpp:35:37)' to 'function<void (Timestamp)> &&' for 1st argument

涉及到 std::function、Lambda 表达式 strictness (严格性)std::bind loose behavior**(宽松行为)**的区别。

一、问题分析

  • using ReadEventCallback = std::function<void(Timestamp)>; 这意味着 ReadEventCallback 必须接受 一个 Timestamp 类型的参数

  • Lambda 写法: [this]() { handleRead(); } 这是一个 无参 的函数对象。它接受 0 个参数

  • 冲突: 当底层(比如 Channel 类)尝试调用这个回调时,它会传入一个 Timestamp(例如 readCallback_(receiveTime))。

    • Lambda 说:"我不接受任何参数"。
    • 编译器报错:"无法将一个'无参调用'转换为'需要一个 Timestamp 参数的调用'"。

但是为什么bind却可以通过? 你发现下面这行代码可以编译通过:

less 复制代码
wakeupChannel_->setReadCallback(std::bind(&EventLoop::handleRead, this));

哪怕 EventLoop::handleRead 本身并没有参数!

这是因为 std::bind 具有"参数丢弃"的特性。

  • Lambda 的行为(严格): Lambda 生成的匿名类是非常严格的。如果你声明它不接受参数 (),那么试图传给它任何参数都会导致编译错误。
  • std::bind 的行为(宽松): std::bind 生成的函数对象可以接受 任意多 的参数。当它被调用时,它只会提取你在 bind 时指定的占位符(如 std::placeholders::_1)对应的参数。多余的参数会被静默丢弃(ignored)。

场景还原:

  1. 底层调用:callback(timestamp)
  2. 如果是 Lambda: 编译器检查 timestamp 是否能传给 () ------ 失败
  3. 如果是 Bind: 绑定对象接收到 timestamp。但因为它内部绑定的 EventLoop::handleRead 不需要参数,且你没有使用 _1 占位符,std::bind 就直接把 timestamp 扔掉了,然后调用 this->handleRead()

二、简单的DEMO

cpp 复制代码
#include <iostream>
#include <functional>

// 1. 定义一个回调类型:它要求必须接受一个 int 参数
using Callback = std::function<void(int)>;

// 2. 模拟底层库 (Channel/TcpConnection)
//    它在调用回调时,一定会塞进去一个参数
void performAction(Callback cb) {
    int eventData = 999; 
    cb(eventData); 
}

// 3. 这个函数不需要任何参数!
void myTask() {
    std::cout << "SUCCESS: myTask 被调用了 (完全无视了传入的 int)" << std::endl;
}

int main() {
    // ---------------------------------------------------------
    // 场景 A: 使用 std::bind (宽松模式) -> 编译通过,运行成功
    // ---------------------------------------------------------
    std::cout << "--- 测试 std::bind ---" << std::endl;
    // bind 发现 myTask 不需要参数,它会自动把 performAction 传进来的 '999' 丢进垃圾桶
    performAction(std::bind(&myTask)); 

    
    // ---------------------------------------------------------
    // 场景 B: 使用普通 Lambda (严格模式) -> 编译报错!
    // ---------------------------------------------------------
    std::cout << "\n--- 测试 Lambda (错误写法) ---" << std::endl;
    // 下面这行如果解开注释,会报错:No viable conversion
    // 因为 performAction 试图传 int,但 Lambda () 拒绝接收
    
    // performAction([]() { 
    //     myTask(); 
    // });


    // ---------------------------------------------------------
    // 场景 C: 使用正确的 Lambda (手动兼容) -> 编译通过
    // ---------------------------------------------------------
    std::cout << "\n--- 测试 Lambda (正确写法) ---" << std::endl;
    // 我们手动写上 (int), 哪怕我们在大括号里根本不用它
    // 这就是显式地告诉编译器:"我知道你会传个 int 给我,我接住它,然后丢掉"
    performAction([](int placeholder) { 
        myTask(); 
    });

    return 0;
}
相关推荐
code_pgf4 小时前
C++11 / C++14 / C++17 / C++20 新特性总结
c++·c++20
颜x小7 小时前
[C#] C++与c#语法对比
开发语言·c++·c#
汉克老师9 小时前
GESP2026年3月认证C++八级( 第三部分编程题(2、子图最短路)精讲
c++·最短路·floyd·gesp8级
abcd_zjq10 小时前
海康线扫相机使用
c++·海康
十五年专注C++开发10 小时前
C++设计一个函数来求容器元素绝对值之和的平均值
c++·模板·accumulate
SilentSlot10 小时前
【C/C++】手写 DPDK 协议栈(十一):基于 PPS、SYN 比例和源 IP 熵的 DDoS 检测
c语言·c++·tcp/ip
王老师青少年编程11 小时前
csp信奥赛C++高频考点专项训练:【排序算法】案例9:成绩排序
c++·排序算法·csp·高频考点·信奥赛·成绩排序
程序喵大人11 小时前
【C++进阶】STL容器与迭代器 - 05 map 和 set 为什么按键保持有序
开发语言·c++·容器·迭代器·stl
ShineWinsu12 小时前
对于Linux:HTTP中cookie、session的解析
linux·网络·c++·网络协议·http·cookie·session
库克克12 小时前
【C++】 unordered_map 与unordered_set
开发语言·c++