विषय पर बढ़ें

अंतिम अद्यतन:2026-10-12

C++ std::bind बनाम lambda तुलना

इंटरव्यू में अक्सर पूछा जाता है: std::bind और lambda दोनों फ़ंक्शन और उसके arguments को callable object में पैक कर सकते हैं, कौन-सा इस्तेमाल करें? जवाब सबने रट रखा है "lambda इस्तेमाल करो", लेकिन क्यों? readability के अलावा performance का अंतर आख़िर कितना बड़ा है? केवल बातें करने से कुछ नहीं होता, इसलिए benchmark लिखकर माप लिया।

(1) परीक्षण का विषय: एक ही member function Calculator::calculate, जिसे क्रमशः std::bind और lambda से पैक करके एक ही template function f में दिया गया और बार-बार कॉल किया गया;

(2) निष्पक्षता के लिए, f callable को template parameter से लेता है (ताकि std::function के type erasure overhead से निष्कर्ष प्रभावित न हो);

(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 type लौटाता है वह "बंधे हुए callable + हर placeholder argument" को सदस्यों के रूप में रखता है; कॉल को standard library के भीतर लंबी forwarding श्रृंखला से गुज़रना पड़ता है, और पुराने implementations में bind objects की copy semantics भारी थी और inlining कठिन था। lambda बस एक closure object है: जो capture करता है वही रखता है, और compiler बिना किसी रुकावट के inline कर देता है।

(2) और भी छिपा हुआ बिंदु: std::bind का argument evaluation क्रम, overload resolution, default arguments पर व्यवहार lambda से भिन्न है, और C++11 के दौर में implementations आपस में भी असहमत थे (बाद में standard ने व्यवहार कस दिया)। Effective Modern C++ Item 34 में इसका पूरा सार है: जहाँ lambda चल सके वहाँ bind मत इस्तेमाल करो।

bind के लिए बचा असली स्थान लगभग एक ही है: C++14 से पहले generic lambda और init-capture नहीं थे, तब std::bind के placeholders "arguments पुनर्व्यवस्थित करने/कुछ arguments पहले से बांधने" का काम करते थे। अब यह सब lambda कर सकता है, और ज़्यादा सहज रूप से।

एक प्रश्न: ऊपर के f को template से बदलकर std::function<void(int)> लेने वाला बना दें, तो दोनों benchmark का अंतर कैसे बदलेगा? क्यों?

संबंधित विषय

टिप्पणियाँ