Skip to content
C++
Library
since C++11
Intermediate

std::bind

std::bind, bind_front and bind_back: partial application, argument reordering and placeholders, with version history and when lambdas are the better choice.

std::bindsince C++11

std::bind in <functional> wraps a callable together with a set of bound arguments and placeholders, returning an unspecified-type function object that forwards to the callable with the bound values substituted.

Overview

std::bind performs partial application: it fixes some arguments of a callable, reorders others via placeholders (_1, _2, … in std::placeholders), and returns a new callable. It replaced the C++98 binders bind1st/bind2nd, ptr_fun and mem_fun, which were deprecated in C++11 and removed in C++17.

The family today:

FacilitySinceBinds
std::bindC++11Any positions, with reordering via placeholders
std::bind_frontC++20Leading arguments only
std::bind_backC++23Trailing arguments only

Key semantics of std::bind:

  • Bound arguments are copied (or moved, if passed as rvalues) into the result and stored by decay. Use std::ref/std::cref to store references.
  • Extra arguments passed at call time beyond the highest placeholder are silently discarded.
  • Placeholder _N selects the N-th call-time argument and can appear multiple times.
  • The result is nested-bind aware: a bind expression passed as an argument is evaluated with the same call-time arguments and its result substituted (this "composition" is unique to std::bind and std::is_bind_expression detects it).
  • The return type is unspecified; hold it with auto or std::function.
  • Since C++20, std::bind is constexpr, and std::bind_front is constexpr and noexcept-propagating.

Syntax

cpp
#include <functional>

// C++11
template<class F, class... Args>
/* unspecified */ bind(F&& f, Args&&... args);

// C++11: explicit return type, useful for overloaded or ambiguous callables
template<class R, class F, class... Args>
/* unspecified */ bind(F&& f, Args&&... args);

// C++20
template<class F, class... Args>
constexpr /* unspecified */ bind_front(F&& f, Args&&... args);

// C++23
template<class F, class... Args>
constexpr /* unspecified */ bind_back(F&& f, Args&&... args);

// C++11: placeholders live in std::placeholders
using namespace std::placeholders;   // _1, _2, ..., _N

Since C++20, std::bind_front can also take a non-type template argument form (bind_front<f>(args...)) only in C++26 (P2714), which avoids storing the function pointer.

Examples

Reordering and fixing arguments

cpp
#include <functional>
#include <iostream>

double divide(double num, double den) { return num / den; }

int main() {
    using namespace std::placeholders;                    // C++11

    auto half   = std::bind(divide, _1, 2.0);             // C++11: fix denominator
    auto invert = std::bind(divide, _2, _1);              // C++11: swap arguments

    std::cout << half(10.0)       << '\n';  // 5
    std::cout << invert(2.0, 10.0) << '\n'; // 5  (10 / 2)
}

Binding member functions

The first argument to a bound member-function pointer is the object (pointer, reference wrapper, smart pointer, or value).

cpp
#include <functional>
#include <string>
#include <vector>
#include <algorithm>

struct Widget {
    std::string name;
    bool matches(const std::string& s) const { return name == s; }
};

int main() {
    using namespace std::placeholders;
    std::vector<Widget> ws{{"a"}, {"b"}, {"c"}};

    // C++11: bind member function to a fixed argument; _1 is the object
    auto it = std::find_if(ws.begin(), ws.end(),
                           std::bind(&Widget::matches, _1, "b"));

    // C++11: bind the object, leave the argument free
    Widget w{"x"};
    auto check = std::bind(&Widget::matches, &w, _1);
    bool r = check("x");   // true
    (void)it; (void)r;
}

Callbacks with context (async I/O style)

cpp
#include <functional>
#include <memory>
#include <system_error>

struct Session { std::size_t total = 0; };

void on_write(const std::error_code& ec, std::size_t n,
              std::shared_ptr<Session> s) {
    if (!ec) s->total += n;
}

using Handler = std::function<void(const std::error_code&, std::size_t)>;

Handler make_handler(std::shared_ptr<Session> s) {
    using namespace std::placeholders;
    return std::bind(on_write, _1, _2, std::move(s));   // C++11
}

Passing references explicitly

cpp
#include <functional>
#include <string>
#include <vector>
#include <algorithm>

void append(std::string& out, char c) { out += c; }

int main() {
    using namespace std::placeholders;
    std::vector<char> v{'c', 'a', 't'};
    std::string out;
    // Without std::ref, `out` would be copied and the result lost.
    std::for_each(v.begin(), v.end(), std::bind(append, std::ref(out), _1)); // C++11
}

bind_front and bind_back

cpp
#include <functional>
#include <cassert>

constexpr int sub(int a, int b) { return a - b; }

int main() {
    constexpr auto from10 = std::bind_front(sub, 10);  // C++20: sub(10, x)
    static_assert(from10(3) == 7);

    constexpr auto minus3 = std::bind_back(sub, 3);    // C++23: sub(x, 3)
    static_assert(minus3(10) == 7);
}

Emulating move capture in C++11

Before C++14 init-capture, std::bind was the way to move an object into a lambda-like callable:

cpp
#include <functional>
#include <vector>

void run() {
    std::vector<double> data(1000);
    auto f = std::bind(                                  // C++11 emulation of init capture
        [](const std::vector<double>& d) { /* use d */ },
        std::move(data));
}

// C++14 replacement:
// auto f = [d = std::move(data)] { /* use d */ };

Best Practices

  • Prefer lambdas in C++14 and later. Lambdas are usually easier to read, inline more reliably, support generic parameters, init-capture and overload resolution, and produce better diagnostics. Reordering is just [](auto n, auto d) { return divide(d, n); }.
  • Use std::bind_front (C++20) for simple leading-argument binding. It has no placeholders, forwards value category correctly, preserves noexcept, and is far cheaper to compile than std::bind.
  • Use std::bind_back (C++23) for trailing-argument binding, replacing the bind(f, _1, x) idiom.
  • Store the result in auto, not std::function, unless you need type erasure. std::function adds an indirection and possible allocation.
  • Wrap by-reference arguments in std::ref/std::cref and make sure the referent outlives the bound object.
  • Pass a shared_ptr (or weak_ptr check) for async callbacks rather than a raw this, so the target outlives the operation.

Common Pitfalls

  • Silent copies. std::bind(f, x) stores a copy of x, even if f takes T&. The call then modifies the stored copy (or fails to compile if f needs a non-const lvalue and the stored object is const-accessed). Use std::ref.
  • Bound arguments are passed as lvalues. The stored copy is passed to the callable as an lvalue, so a callable taking T&& will not compile with a bound value. Lambdas with init-capture and std::move avoid this; so does bind_front, which forwards according to the wrapper's own value category.
  • Overloaded functions. std::bind(overloaded, _1) cannot deduce which overload to take. Cast the function pointer, give an explicit signature, or use a lambda.
  • Placeholders are evaluated at call time, other arguments at bind time. A subexpression such as std::bind(f, g()) calls g() immediately, not per invocation. Wrap it in std::bind(g) to defer, which makes use of nested-bind evaluation, or use a lambda.
  • Nested bind surprises. Passing a bind expression as an argument triggers evaluation rather than being treated as a value. To pass a bind result as data, wrap it in std::ref or a std::function.
  • Too many call-time arguments are ignored. std::bind(f, _1)(1, 2, 3) compiles and drops 2 and 3. This hides arity bugs that a lambda would reject.
  • Dangling object pointers. Binding &obj or this stores a raw pointer; if the bound callable outlives the object, invocation is undefined behavior.
  • Removed C++98 binders. bind1st, bind2nd, ptr_fun, mem_fun and mem_fun_ref are gone in C++17. Replace them with lambdas, std::bind, std::bind_front, or std::mem_fn (C++11).
  • Compile-time cost. Heavy std::bind use inflates template instantiation and error messages; deep nesting of bind expressions is especially expensive.

Migrating from bind1st/bind2nd

cpp
#include <algorithm>
#include <functional>
#include <vector>

int count_greater_than_5(const std::vector<int>& v) {
    // C++98/03 (removed in C++17):
    //   std::count_if(v.begin(), v.end(), std::bind2nd(std::greater<int>(), 5));

    // C++11:
    //   std::count_if(v.begin(), v.end(), std::bind(std::greater<int>(), _1, 5));

    // C++14 and later, preferred:
    return std::count_if(v.begin(), v.end(), [](int x) { return x > 5; });
}

See Also

  • std::any, type-erased value storage, often paired with callback registries
  • std::function (C++11): type-erased callable wrapper that commonly holds bind results
  • std::mem_fn (C++11): adapts member pointers into callables
  • std::ref / std::cref (C++11): reference wrappers for bound arguments
  • std::move_only_function (C++23): move-only alternative to std::function for bound move-only state