5.2.23 · D4 · HinglishC++ Programming

Exercisesstd - function and std - bind

2,507 words11 min read↑ Read in English

5.2.23 · D4 · Coding › C++ Programming › std - function and std - bind

Poore note mein ye assume karo:

#include <functional>
#include <iostream>
#include <vector>
using namespace std::placeholders;   // gives us _1, _2, ...

L1 — Recognition

Goal: pehchano ki kya kahan fit hota hai, koi tracing nahi chahiye.

Exercise 1.1 — Kaun sa signature?

Aapke paas int add(int a, int b) hai. Blank fill karo taaki yeh compile ho:

std::function<______> f = add;
Recall Solution

Answer: int(int,int). std::function ka template parameter function type ki tarah likha jaata hai, normal template arguments ki tarah nahi. add int return karta hai aur do ints leta hai, isliye iska call signature int(int,int) hai. Poori line: std::function<int(int,int)> f = add;

Exercise 1.2 — Woh callable dhundho jo fit NAHI hota

Inme se kaun std::function<double(double)> mein store ho sakta hai?

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; }      // D
Recall Solution

A, B, C fit karte hain. D nahi karta. std::function<double(double)> kuch bhi accept karta hai jo callable ho aur ek double in, ek double out ho.

  • sq — exactly match karta hai.
  • lam — ek lambda jo ek double leta hai, double return karta hai.
  • Neg{} — ek functor jiska operator() match karta hai.
  • badcount do ints leta hai — galat argument count, isliye isko wrap nahi kiya ja sakta. std::function call shape check karta hai, aur yeh shape galat hai.

L2 — Application

Goal: khud sahi expression likho.

Exercise 2.1 — Teen callables ek vector mein store karo

Aisa code likho jo in teeno tasks ko ek std::vector mein daale aur inhe order mein run kare:

  • ek lambda jo "boot" print kare,
  • ek lambda jo "load" print kare,
  • ek free function void shutdown() jo "halt" print kare.
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

Kyun kaam karta hai: har lambda ka apna alag unnamed type hota hai, isliye directly lambdas ka vector banana impossible hai. std::function<void()> teeno ko ek hi type deta hai jo sirf void() signature pe based hai.

Exercise 2.2 — bind se ek argument fix karo

int power(int base, int exp) (jo base^exp compute karta hai) diya hai, std::bind use karke ek callable square banao taaki square(n), n*n return kare.

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) = 25

Kyun: hum chahte hain ki base caller se aaye aur exponent 2 pe lock ho jaaye. Toh base placeholder _1 ban jaata hai (pehla call argument) aur exp fixed value 2 hai. square(5) expand hota hai power(5, 2) = 25 mein.


L3 — Analysis

Goal: exactly trace karo kya run hoga; bug dhundho.

Exercise 3.1 — Output predict karo (reordering)

int sub(int a, int b){ return a - b; }
auto g = std::bind(sub, _2, _1);
std::cout << g(3, 10) << "\n";

Kya print hoga?

Figure — std - function and std - bind
Recall Solution

7 print hoga. _n ko padhein jaise "caller ka n-th argument". Caller (3, 10) supply karta hai, toh _1 = 3 aur _2 = 10.

  • Pehla bind slot hai _2a mein jaata hai → a = 10.
  • Doosra bind slot hai _1b mein jaata hai → b = 3.

Toh g(3,10) ban jaata hai sub(10, 3) = 7. Figure dekho: crossing arrows poora point hain — _2 aur _1 caller ke inputs ko sub tak pahunchne se pehle swap kar dete hain.

Exercise 3.2 — 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";

Kya print hoga, aur yeh surprising kyun hai?

Recall Solution

0 print hoga. std::bind apne arguments ko value se copy karta hai. acc likhna (na ki &acc) bind ko accumulator ki ek copy deta hai. f ko har call woh hidden internal copy ko mutate karta hai, asli acc ko kabhi nahi. Toh acc.total 0 rehta hai. Do sahi fixes:

auto f = std::bind(&Accumulator::add, &acc, _1);        // pointer pass karo
auto g = std::bind(&Accumulator::add, std::ref(acc), _1); // reference wrapper pass karo

Dono taraf se calls ab asli acc tak pahunchenge, aur 10 print hoga. (Dekho std::ref and std::cref.)

Exercise 3.3 — Empty-function crash

std::function<int(int)> f;   // never assigned
std::cout << f(42) << "\n";

Runtime pe kya hoga?

Recall Solution

Yeh std::bad_function_call throw karega (program terminate hoga jab tak aap isse catch na karo). Ek empty std::function mein koi target nahi hota, toh invoke karne ke liye kuch hai hi nahi. Fix — hamesha guard karo:

if (f) std::cout << f(42) << "\n";
else   std::cout << "no target\n";

if (f) tab hi true hota hai jab std::function currently ek callable hold kare.


L4 — Synthesis

Goal: pieces ko ek chota working design mein combine karo.

Exercise 4.1 — Ek tiny event dispatcher

Ek class Button banao jo void() callbacks ki list (onClick) store kare, aur zyada register karne de, aur click() se sab fire kare. Phir do lambdas aur ek bound member function Logger::log register karo.

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

Kyun design kaam karta hai: std::function<void()> woh uniform "shape" hai jis par har handler agree karta hai. Lambdas, free functions, aur bound member functions sab us ek type mein collapse ho jaate hain, isliye woh ek hi vector mein khushi se rehte hain. Yeh bilkul Callbacks and Event Systems pattern hai. (Note karo ki &Logger::log ko ek object chahiye — yahan pointer &lg — apna first bound argument ke roop mein, kyunki member function secretly pehle this leta hai.)

Exercise 4.2 — bind + placeholders compose karke "clamp lower bound" banao

int maxOf(int a, int b){ return a>b ? a : b; } diya hai, atLeast5 banao taaki atLeast5(x) x return kare agar x >= 5 ho, warna 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) = 9

Kyun: max(x, 5) exactly hai "kabhi 5 se neeche nahi". Caller ke argument ko a mein route karte hain (_1 se) aur floor 5 ko b mein lock karte hain. Dono orderings (_1,5 ya 5,_1) yahan same answer dete hain kyunki max symmetric hai — ek accha self-check ki aapne mapping samajh li.


L5 — Mastery

Goal: cost, alternatives, aur subtle semantics ke baare mein reason karo.

Exercise 5.1 — Cost reasoning

Aapka inner loop ek callback 10 million times call karta hai. Version A std::function<int(int)> leta hai; version B template<class F> ... F f hai. Kaun faster hai, aur kyun — ek precise sentence mein ki compiler help kyon kar/nahi kar sakta?

Recall Solution

Version B (template) faster hai. Ek template callable ke concrete type ke saath instantiate hota hai, isliye compiler call site par exact code dekhta hai aur use inline kar sakta hai. std::function concrete type ko type erasure ke peeche chhupa deta hai (dekho Templates and Type Erasure): call ek indirect / virtual-like dispatch se jaati hai jo inline nahi ho sakti, aur bade targets ke liye isko construct karne mein heap-allocation ho sakti hai. Toh std::function ek chhota runtime cost de kar unknown types ko uniformly store karne ki ability paata hai — ise sirf tab use karo jab aapko actually woh uniformity chahiye.

Exercise 5.2 — bind vs lambda equivalence

std::bind(sub, _2, _1) (Exercise 3.1 se) ko ek modern lambda ki tarah rewrite karo, aur ek reason batao ki lambdas usually kyun prefer ki jaati hain.

Recall Solution
auto g = [](int x, int y){ return sub(y, x); };   // g(3,10) -> sub(10,3) = 7

Prefer karne ka reason: lambda argument routing ko directly code mein spell out karta hai jo aap padhh sakte ho (sub(y, x)), jabki _2, _1 reader ko placeholder positions mentally decode karne par majboor karta hai. Lambdas compiler ke liye inline karna bhi aasaan hain aur std::bind ki machinery nahi kheenchte. Wide callable landscape ke liye dekho Lambda Expressions vs Function Pointers.

Exercise 5.3 — Trickiest output predict karo

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";

Kya print hoga? (Har slot dekho.)

Figure — std - function and std - bind
Recall Solution

527 print hoga. Caller (2, 99, 5) supply karta hai → _1 = 2, _2 = 99, _3 = 5. f ke har parameter (a, b, c) ko uske bind slot se map karo:

  • a_35
  • b_12
  • c ← fixed 7

Toh actual call hai f(5, 2, 7) = 5*100 + 2*10 + 7 = 527. Dhyan do ki _2 = 99 kabhi use nahi hota — placeholders aapko call arguments drop karne bhi dete hain. Figure wiring dikhata hai: teen arrows andar, lekin 99 wali wire simply dead-end ho jaati hai.


Recall Self-test summary

Placeholder _n ka matlab kya hai? ::: Woh n-th argument jo bound object baad mein call hone par supply kiya jaata hai. std::bind se kisi object ka live link kaise rakhte ho? ::: Pointer &obj pass karo ya std::ref(obj) mein wrap karo; plain bind value se copy karta hai. Empty std::function call karne par kya hota hai? ::: std::bad_function_call throw hota hai; if (f) se guard karo. Template callback std::function se usually faster kyun hota hai? ::: Template concrete type rakhta hai, isliye call inline ho sakti hai; std::function type erase karta hai aur indirectly dispatch karta hai.