跳转至

最后更新:2026-10-13

C++ std::bind 与 lambda 对比

面试里常被问:std::bind 和 lambda 都能把函数和参数打包成可调用对象,到底该用哪个?答案大家都会背"用 lambda",但为什么?除了 readability,性能差异到底有多大?光说不练假把式,写了个 benchmark 实测一把。

(1)测试对象:同一个成员函数 Calculator::calculate,分别用 std::bind 和 lambda 打包,传给同一个模板函数 f 反复调用;

(2)为了公平,f 用模板参数接收可调用对象(避免 std::function 的类型擦除开销干扰结论);

(3)代码与结果:https://quick-bench.com/q/CEnR_XUuIwnZxaP4KJNSlpYG38c

#include <functional>
#include <numeric>
#include <vector>



struct Calculator {
    int count{1};
    int total{0};

    auto calculate() -> void {
        using std::iota, std::vector;

        // Random math
        vector<int> u(8), v(Calculator::count * u.size());

        iota(v.begin(), v.end(), *u.begin());

        int sum{*v.begin() + *u.begin()};

        for (size_t pos{0}; pos < v.size(); ++pos) {
            sum += u[pos % u.size()] * v[pos];
        }

        Calculator::total += sum;
        ++Calculator::count;
    }
};

// auto f(std::function<void(int)> g) -> void { g(rand()); }

template<typename FN> auto f(FN g) -> void { g(rand()); }


static auto BM_bind(benchmark::State& state) -> void {
    Calculator calculator;
    for (auto _ : state) {
        f(std::bind(&Calculator::calculate, &calculator));
    }
}

static auto BM_lambda(benchmark::State& state) -> void {
    Calculator calculator;
    for (auto _ : state) {
        f([&calculator]([[maybe_unused]] int x) { calculator.calculate(); });
    }
}

BENCHMARK(BM_bind);
BENCHMARK(BM_lambda);

结果怎么看

跑出来 lambda 版本明显快于 bind 版本,原因主要有两点:

(1)std::bind 返回的 binder 类型把"绑定的可调用对象 + 各个占位参数"全部存成成员,调用时要经过标准库内部一长串转发,且早期实现里 bind 对象拷贝语义重、内联困难;lambda 就是一个闭包对象,捕获什么就存什么,编译器内联毫无障碍。

(2)更隐蔽的一点:std::bind 对**参数求值顺序、重载决议、默认参数**的处理都和 lambda 不同,C++11 时代还各实现不一致(后来标准收紧了行为)。Effective Modern C++ Item 34 对此有完整总结:能用 lambda 就别用 bind。

bind 真正还剩的场景基本只有一个:C++14 之前没有泛型 lambda、没有 init-capture 时,std::bind 的占位符能做"参数重排/预绑定部分参数"的活。现在这些 lambda 都能干,而且更直观。

留个思考题:把上面的 f 从模板改成接收 std::function<void(int)>,两个 benchmark 的差距会怎么变?为什么?

相关主题

评论