This page is a drill . We enumerate every shape of situation std::function and std::bind can put in front of you, then work one example per shape. If you have not met the wrapper idea yet, read the parent std::function and std::bind first — this page assumes you know that std::function<R(Args...)> stores any callable matching the signature, and that std::bind pre-fills arguments.
Before we start, one word we will lean on constantly: arity . The arity of a callable is simply how many arguments it takes . sub(a,b) has arity 2. When we bind, we usually lower the arity — we hand over fewer arguments because some are remembered in advance. Keep that picture in mind:
Every case below is a cell . The worked examples are labelled with the cell they hit, and together they cover the whole grid.
Cell
What it stresses
Callable kind
bind action
C1
Store & swap different callables in one variable
free fn / lambda / functor
none
C2
Container of callbacks (heterogeneous types)
lambdas
none
C3
Fix one argument (arity 2 → 1)
free fn
fix + _1
C4
Reorder arguments (no arity change)
free fn
_2, _1
C5
Drop an argument entirely (arity down, unused call arg)
free fn
fix only
C6
Bind a member function (hidden this)
member fn
object + _1
C7
Reference semantics — copy vs std::ref
lambda over state
fix by ref
C8
Degenerate: empty std::function called
none
none
C9
Limiting: arity 0, and nesting bind-in-bind
free fn
_1 reuse
C10
Exam twist — evaluation order of side effects
free fn
_1, _1
Worked example Store a free function, a lambda, and a functor in the same
std::function, calling each with (6, 2).
int add ( int a , int b ){ return a + b; }
struct Mul { int operator () ( int a , int b ) const { return a * b; } };
std ::function <int ( int , int ) > f;
f = add; // (1)
f = []( int a , int b ){ return a - b;}; // (2)
f = Mul{}; // (3)
Forecast: guess the result of f(6,2) after each assignment before reading on.
f = add — Why this step? add has signature int(int,int), exactly the wrapper's signature, so it is accepted. f(6,2) = add(6,2) = 8 .
f = lambda — Why? The lambda is a different type from add, but its call shape int(int,int) still matches. Assigning replaces the stored callable. f(6,2) = 6-2 = 4 .
f = Mul{} — Why? Mul::operator() is callable as int(int,int). Now f(6,2) = 6*2 = 12 .
Verify: three assignments, three answers 8, 4, 12 . Each stored value was thrown away on the next assignment (only one lives at a time). Units: all plain int, no surprises.
Worked example Push three lambdas into a
vector, run them, count characters printed.
std ::vector < std ::function <void () >> tasks;
tasks. push_back ([]{ std ::cout << "AB" ; }); // 2 chars
tasks. push_back ([]{ std ::cout << "CDE" ; }); // 3 chars
tasks. push_back ([]{ std ::cout << "F" ; }); // 1 char
for ( auto& t : tasks) t ();
Forecast: how many characters total, and could a raw vector<lambda> do this?
Each lambda has its own unnamed type . Why bind them to std::function? A vector needs one element type; std::function<void()> is that one type. Without it, the three lambdas cannot share a container.
The loop invokes each in insertion order → prints AB, CDE, F.
Verify: total chars = 2 + 3 + 1 = 6 . Output string = "ABCDEF" (length 6). ✔
sub(a,b)=a-b, make sub10 that always uses a=10. Evaluate sub10(3).
using namespace std :: placeholders ;
auto sub10 = std :: bind (sub, 10 , _1);
sub10 ( 3 );
Forecast: is the answer 10 − 3 or 3 − 10 ?
10 sits in position a of sub. Why this step? A bare value in a bind expression is used as-is , glued to that parameter slot. So a is locked to 10.
_1 sits in position b. Why? _1 means "the first argument the caller supplies later." When we call sub10(3), that 3 flows into b.
Effective call: sub(10, 3).
Verify: sub10(3) = 10 - 3 = 7 . Arity dropped from 2 to 1. ✔
flip that swaps the order of sub's args. Evaluate flip(3, 10).
auto flip = std :: bind (sub, _2, _1);
flip ( 3 , 10 );
Forecast: 3-10 or 10-3?
_2 sits in a. Why? _2 = the second call argument = 10. So a = 10.
_1 sits in b. Why? _1 = the first call argument = 3. So b = 3.
Effective call: sub(10, 3).
Verify: flip(3,10) = 10 - 3 = 7 . Same answer as C3 but built by reordering not by fixing — no argument was dropped, arity stayed 2. ✔
sub fully fixed to (20, 4). Call it with a junk extra argument. What happens?
auto twenty_minus_four = std :: bind (sub, 20 , 4 );
twenty_minus_four ( 999 ); // the 999 is IGNORED
Forecast: does the extra 999 cause an error, or vanish?
Both slots are fixed values (20, 4); there is no placeholder. Why this matters? With no _n, none of the call arguments are wired to sub.
Extra call arguments to a bound object that aren't referenced by any placeholder are simply discarded — this is legal.
Effective call: sub(20, 4).
Verify: result = 20 - 4 = 16 , regardless of what you pass. Arity of the bound object is effectively 0 for the computation. ✔
Counter{base=100} has int addBase(int x){ return base + x; }. Bind it to object c and call with 5.
struct Counter { int base; int addBase ( int x ) const { return base + x; } };
Counter c{ 100 };
auto f = std :: bind ( & Counter ::addBase, & c, _1);
f ( 5 );
Forecast: what is the first bound argument here — is it 5?
&Counter::addBase — Why the &Class::? A member function isn't a plain value; you name it with a member-function pointer.
&c is the first bound argument. Why? Every member function secretly receives the object as a hidden first parameter (this). Bind makes that hidden parameter explicit , so the object goes first.
_1 fills x. Calling f(5) puts 5 into x.
Effective call: c.addBase(5) = 100 + 5.
Verify: f(5) = 105 . The visible arity of addBase is 1, but the bound arity is also 1 because the object was fixed. ✔
int n = 0 and a lambda that increments a referenced int. Bind by value, then by std::ref, and observe n.
int n = 0 ;
auto bump = []( int& r ){ r += 1 ; };
auto by_copy = std :: bind (bump, n); // BUG intent: bumps a COPY
auto by_ref = std :: bind (bump, std :: ref (n)); // bumps the real n
by_copy (); by_copy (); // n unchanged
by_ref (); by_ref (); // n = 2
Forecast: after both blocks, what is n?
std::bind(bump, n) copies n into the bound object. Why? std::bind stores its arguments by value by default. So by_copy() increments an internal copy — the real n never moves.
std::bind(bump, std::ref(n)) stores a reference wrapper . Why std::ref? It is the only way to tell bind "keep a handle to the original, don't copy." See std::ref and std::cref .
Two calls to by_ref add 1 each → n goes 0 → 1 → 2.
Verify: final n = 2 (the two by_copy() calls contributed 0 ). ✔
std::function that was never assigned a callable — what does calling it do?
std ::function <int ( int ) > f; // empty
if (f) { /* ... */ } // false
f ( 5 ); // throws!
Forecast: returns 0? returns garbage? or throws?
A default-constructed std::function holds nothing . Why guard with if (f)? The bool test tells you whether a target is stored.
Invoking the empty wrapper has no target to forward to, so it throws std::bad_function_call.
Verify: the exception type is exactly std::bad_function_call; static_cast<bool>(f) is false for an empty one. The safe pattern is if (f) f(5);. ✔
Worked example (a) Bind a nullary-after-fixing function; (b) reuse
_1 twice.
int nine (){ return 9 ; }
auto g = std :: bind (nine); // (a) arity 0, stays 0
int addTriple ( int a , int b , int c ){ return a + b + c; }
auto twice = std :: bind (addTriple, _1, _1, 10 ); // (b) _1 used twice
twice ( 4 );
Forecast: g() = ? and twice(4) = ?
std::bind(nine) — Why bother? It just wraps a zero-argument call; the bound object also takes zero args. Nothing is fixed, nothing is placeholdered. g() = 9 .
_1 may appear multiple times . Why? A placeholder is a substitution rule , not a consume-once token; the same call argument can feed several slots.
twice(4) → addTriple(4, 4, 10).
Verify: g() = 9 . twice(4) = 4 + 4 + 10 = 18 . ✔
Worked example A logging function records order; bind reuses
_1. Predict the recorded values.
int record ( int a , int b ){ return 100 * a + b; } // encodes (a,b) as one int
auto weird = std :: bind (record, _1, _2);
weird ( 7 , 3 );
Forecast: does _1 grab 7 or 3?
_1 → first call arg 7 → parameter a.
_2 → second call arg 3 → parameter b.
Effective call record(7, 3) returns 100*7 + 3.
Verify: weird(7,3) = 703. The encoding lets us prove which slot got which value : hundreds digit = a = 7, ones = b = 3. ✔
Recall Cover the matrix from memory
Which cell tests dropping an argument? ::: C5 — all slots fixed, extra call args ignored.
Which cell throws? ::: C8 — calling an empty std::function throws std::bad_function_call.
How do you keep a real reference through std::bind? ::: Wrap the arg in std::ref (C7); bind copies by default.
Can _1 appear twice in one bind expression? ::: Yes — it's a substitution rule, so the same call arg feeds every _1 slot (C9b).
Mnemonic Placeholder rule, one line
"_n = the n-th arg the caller gives later — fixed values are frozen, placeholders are wires."
See also: Lambda Expressions , Function Pointers , Functors and operator() , Templates and Type Erasure , Callbacks and Event Systems .