Exercises — std - function and std - bind
Throughout, assume:
#include <functional>
#include <iostream>
#include <vector>
using namespace std::placeholders; // gives us _1, _2, ...L1 — Recognition
Goal: recognise what fits where, no tracing required.
Exercise 1.1 — Which signature?
You have int add(int a, int b). Fill the blank so this compiles:
std::function<______> f = add;Recall Solution
Answer: int(int,int).
The template parameter of std::function is written like a function type, not like normal template arguments. add returns int and takes two ints, so its call signature is int(int,int). Full line: std::function<int(int,int)> f = add;
Exercise 1.2 — Spot the callable that does NOT fit
Which of these can be stored in std::function<double(double)>?
double sq(double x){ return x*x; } // A
auto lam = [](double x){ return x + 1.0; }; // B
struct Neg { double operator()(double x) const { return -x; } }; // C
int badcount(int a, int b){ return a+b; } // DRecall Solution
A, B, C fit. D does not.
std::function<double(double)> accepts anything callable as one double in, one double out.
sq— matches exactly.lam— a lambda taking onedouble, returningdouble.Neg{}— a functor whoseoperator()matches.badcounttakes twoints — wrong argument count, so it cannot be wrapped.std::functionchecks the call shape, and the shape is wrong.
L2 — Application
Goal: write the correct expression yourself.
Exercise 2.1 — Store three callables in one vector
Write code that puts these three tasks into a single std::vector and runs them in order:
- a lambda printing
"boot", - a lambda printing
"load", - a free function
void shutdown()that prints"halt".
Recall Solution
void shutdown(){ std::cout << "halt\n"; }
std::vector<std::function<void()>> tasks;
tasks.push_back([]{ std::cout << "boot\n"; });
tasks.push_back([]{ std::cout << "load\n"; });
tasks.push_back(shutdown);
for (auto& t : tasks) t();Output:
boot
load
halt
Why this works: each lambda has its own unique unnamed type, so a vector of lambdas is impossible directly. std::function<void()> gives all three the same type based only on the signature void().
Exercise 2.2 — Fix one argument with bind
Given int power(int base, int exp) (computes base^exp), build a callable square such that square(n) returns n*n, using std::bind.
Recall Solution
int power(int base, int exp){
int r = 1;
for (int i = 0; i < exp; ++i) r *= base;
return r;
}
auto square = std::bind(power, _1, 2); // exp fixed = 2, base = _1
// square(5) -> power(5, 2) = 25Why: we want the base to come from the caller and the exponent to be locked at 2. So base becomes the placeholder _1 (the first call argument) and exp is the fixed value 2. square(5) expands to power(5, 2) = 25.
L3 — Analysis
Goal: trace exactly what runs; find the bug.
Exercise 3.1 — Predict the output (reordering)
int sub(int a, int b){ return a - b; }
auto g = std::bind(sub, _2, _1);
std::cout << g(3, 10) << "\n";What prints?

Recall Solution
Prints 7.
Read _n as "the n-th argument the caller supplies". The caller supplies (3, 10), so _1 = 3 and _2 = 10.
- First bind slot is
_2→ goes intoa→a = 10. - Second bind slot is
_1→ goes intob→b = 3.
So g(3,10) becomes sub(10, 3) = 7. Look at the figure: the crossing arrows are the whole point — _2 and _1 swap the caller's inputs before they reach sub.
Exercise 3.2 — The copy-vs-reference bug
struct Accumulator {
int total = 0;
void add(int x){ total += x; }
};
Accumulator acc;
auto f = std::bind(&Accumulator::add, acc, _1); // note: acc, not &acc
f(5);
f(5);
std::cout << acc.total << "\n";What prints, and why is it surprising?
Recall Solution
Prints 0.
std::bind copies its arguments by value. Writing acc (not &acc) hands bind a copy of the accumulator. Every call to f mutates that hidden internal copy, never the original acc. So acc.total stays 0.
Two correct fixes:
auto f = std::bind(&Accumulator::add, &acc, _1); // pass a pointer
auto g = std::bind(&Accumulator::add, std::ref(acc), _1); // pass a reference wrapperEither way, calls now reach the real acc, and it would print 10. (See std::ref and std::cref.)
Exercise 3.3 — The empty-function crash
std::function<int(int)> f; // never assigned
std::cout << f(42) << "\n";What happens at runtime?
Recall Solution
It throws std::bad_function_call (the program terminates unless you catch it). An empty std::function holds no target, so there is nothing to invoke.
Fix — always guard:
if (f) std::cout << f(42) << "\n";
else std::cout << "no target\n";if (f) is true only when the std::function currently holds a callable.
L4 — Synthesis
Goal: combine the pieces into a small working design.
Exercise 4.1 — A tiny event dispatcher
Build a class Button that stores a list of void() callbacks (onClick), lets you register more, and fires them all with click(). Then register two lambdas and one bound member function Logger::log.
Recall Solution
struct Logger {
void log() const { std::cout << "logged\n"; }
};
struct Button {
std::vector<std::function<void()>> handlers;
void onClick(std::function<void()> h){ handlers.push_back(std::move(h)); }
void click(){ for (auto& h : handlers) h(); }
};
int main(){
Logger lg;
Button b;
b.onClick([]{ std::cout << "sound\n"; });
b.onClick([]{ std::cout << "ripple\n"; });
b.onClick(std::bind(&Logger::log, &lg)); // bound member fn
b.click();
}Output:
sound
ripple
logged
Why the design works: std::function<void()> is the uniform "shape" every handler agrees on. Lambdas, free functions, and bound member functions all collapse into that one type, so they live happily in the same vector. This is exactly the Callbacks and Event Systems pattern. (Note &Logger::log needs an object — here the pointer &lg — as its first bound argument, since a member function secretly receives this first.)
Exercise 4.2 — Compose bind + placeholders to make a "clamp lower bound"
Given int maxOf(int a, int b){ return a>b ? a : b; }, build atLeast5 such that atLeast5(x) returns x if x >= 5, else 5.
Recall Solution
auto atLeast5 = std::bind(maxOf, _1, 5); // a = caller's x, b = 5
// atLeast5(3) -> maxOf(3,5) = 5
// atLeast5(9) -> maxOf(9,5) = 9Why: max(x, 5) is exactly "never below 5". We route the caller's argument into a (via _1) and lock the floor 5 into b. Both orderings (_1,5 or 5,_1) give the same answer here because max is symmetric — a nice self-check that you understood the mapping.
L5 — Mastery
Goal: reason about cost, alternatives, and subtle semantics.
Exercise 5.1 — Cost reasoning
Your inner loop calls a callback 10 million times. Version A takes std::function<int(int)>; version B is template<class F> ... F f. Which is faster, and why — in one precise sentence about why the compiler can/can't help?
Recall Solution
Version B (the template) is faster.
A template instantiates with the callable's concrete type, so the compiler sees the exact code at the call site and can inline it. std::function hides the concrete type behind type erasure (see Templates and Type Erasure): the call goes through an indirect / virtual-like dispatch that cannot be inlined, and constructing it may heap-allocate for large targets. So std::function trades a small runtime cost for the ability to store unknown types uniformly — use it only when you actually need that uniformity.
Exercise 5.2 — bind vs lambda equivalence
Rewrite std::bind(sub, _2, _1) (from Exercise 3.1) as a modern lambda, and state one reason lambdas are usually preferred.
Recall Solution
auto g = [](int x, int y){ return sub(y, x); }; // g(3,10) -> sub(10,3) = 7Reason preferred: the lambda spells out the argument routing directly in code you can read (sub(y, x)), whereas _2, _1 forces the reader to mentally decode placeholder positions. Lambdas are also easier for the compiler to inline and don't drag in std::bind's machinery. See Lambda Expressions vs Function Pointers for the wider callable landscape.
Exercise 5.3 — Predict the trickiest output
int f(int a, int b, int c){ return a*100 + b*10 + c; }
auto h = std::bind(f, _3, _1, 7);
std::cout << h(2, 99, 5) << "\n";What prints? (Watch every slot.)

Recall Solution
Prints 527.
Caller supplies (2, 99, 5) → _1 = 2, _2 = 99, _3 = 5.
Map each of f's parameters (a, b, c) to its bind slot:
a←_3→5b←_1→2c← fixed7
So the actual call is f(5, 2, 7) = 5*100 + 2*10 + 7 = 527.
Notice _2 = 99 is never used — placeholders let you drop call arguments too. The figure shows the wiring: three arrows in, but the 99 wire simply dead-ends.
Recall Self-test summary
Placeholder _n means what? ::: The n-th argument supplied when the bound object is later called.
How do you keep a live link to an object through std::bind? ::: Pass a pointer &obj or wrap in std::ref(obj); plain bind copies by value.
What does calling an empty std::function do? ::: Throws std::bad_function_call; guard with if (f).
Why is a template callback usually faster than std::function? ::: The template keeps the concrete type, so the call can be inlined; std::function erases the type and dispatches indirectly.