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++11std::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:
| Facility | Since | Binds |
|---|---|---|
std::bind | C++11 | Any positions, with reordering via placeholders |
std::bind_front | C++20 | Leading arguments only |
std::bind_back | C++23 | Trailing 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::crefto store references. - Extra arguments passed at call time beyond the highest placeholder are silently discarded.
- Placeholder
_Nselects 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::bindandstd::is_bind_expressiondetects it). - The return type is unspecified; hold it with
autoorstd::function. - Since C++20,
std::bindisconstexpr, andstd::bind_frontisconstexprandnoexcept-propagating.
Syntax
#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, ..., _NSince 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
#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).
#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)
#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
#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
#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:
#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, preservesnoexcept, and is far cheaper to compile thanstd::bind. - Use
std::bind_back(C++23) for trailing-argument binding, replacing thebind(f, _1, x)idiom. - Store the result in
auto, notstd::function, unless you need type erasure.std::functionadds an indirection and possible allocation. - Wrap by-reference arguments in
std::ref/std::crefand make sure the referent outlives the bound object. - Pass a
shared_ptr(orweak_ptrcheck) for async callbacks rather than a rawthis, so the target outlives the operation.
Common Pitfalls
- Silent copies.
std::bind(f, x)stores a copy ofx, even ifftakesT&. The call then modifies the stored copy (or fails to compile iffneeds a non-const lvalue and the stored object is const-accessed). Usestd::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 andstd::moveavoid this; so doesbind_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())callsg()immediately, not per invocation. Wrap it instd::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::refor astd::function. - Too many call-time arguments are ignored.
std::bind(f, _1)(1, 2, 3)compiles and drops2and3. This hides arity bugs that a lambda would reject. - Dangling object pointers. Binding
&objorthisstores a raw pointer; if the bound callable outlives the object, invocation is undefined behavior. - Removed C++98 binders.
bind1st,bind2nd,ptr_fun,mem_funandmem_fun_refare gone in C++17. Replace them with lambdas,std::bind,std::bind_front, orstd::mem_fn(C++11). - Compile-time cost. Heavy
std::binduse inflates template instantiation and error messages; deep nesting of bind expressions is especially expensive.
Migrating from bind1st/bind2nd
#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 resultsstd::mem_fn(C++11): adapts member pointers into callablesstd::ref/std::cref(C++11): reference wrappers for bound argumentsstd::move_only_function(C++23): move-only alternative tostd::functionfor bound move-only state