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;
}
相关推荐
小老鼠不吃猫2 小时前
C++20 STL <numbers> 数学常量库
开发语言·c++·c++20
程序员zgh2 小时前
C++常用设计模式
c语言·数据结构·c++·设计模式
im_AMBER2 小时前
Leetcode 80 统计一个数组中好对子的数目
数据结构·c++·笔记·学习·算法·leetcode
尘诞辰3 小时前
【C语言】数据在内存中的储存
c语言·开发语言·数据结构·c++
无敌最俊朗@3 小时前
STL-关联容器(面试复习4)
开发语言·c++
无限进步_3 小时前
【C语言】栈(Stack)数据结构的实现与应用
c语言·开发语言·数据结构·c++·后端·visual studio
闻缺陷则喜何志丹3 小时前
【计算几何 SAT轴】P6732 「Wdsr-2」方分|普及+
c++·数学·计算几何·sat轴·凸多边形分离
embrace993 小时前
【C语言学习】预处理详解
java·c语言·开发语言·数据结构·c++·学习·算法
拼好饭和她皆失3 小时前
《二分答案算法精讲:从原理到实战(上篇)》
c++·算法