5.2.14 · D3 · HinglishC++ Programming

Worked examplesTemplates — function templates, class templates

2,556 words12 min read↑ Read in English

5.2.14 · D3 · Coding › C++ Programming › Templates — function templates, class templates

Prerequisites jo tum open rakhna chahte ho: Function Overloading, Type Deduction (auto), Compile-time vs Runtime, Template Specialization, Macros vs Templates, Generic Programming, STL Containers.


The scenario matrix

Templates mein koi "quadrants" nahi hote jaise angle problem mein hote hain — lekin inka ek fixed set of situations zaroor hota hai jo alag behave karte hain. Yeh har woh cell hai jo hume cover karni hai.

# Case class Yeh jo question test karta hai Covered by
A Function template, clean deduction Compiler args padhta hai, ek T dhundhta hai Ex 1
B Deduction conflict (degenerate input) Do arg types → koi single T nahi Ex 2
C Explicit type argument Tum deduction ko <...> se override karte ho Ex 3
D Class template, explicit <T> No constructor deduction (pre-C++17) Ex 4
E Non-type parameter (compile-time value) <> mein ek number, type nahi Ex 5
F Full specialization (ek type ke liye "galat" wala case) Generic recipe ko override karo Ex 6
G The linker trap (definition in .cpp) Real-world debugging word problem Ex 7
H Exam twist: overload vs template resolution Kaun sa function actually call hota hai? Ex 8

Neeche har example apne cell letter ke saath tagged hai.


Ex 1 — Cell A: function template, clean deduction

Forecast: Aage padhne se pehle har call ke liye T aur returned value guess karo.

  1. Sirf argument types dekho. 3 aur 7 dono int hain. Yeh step kyun? Deduction values ko ignore karta hai — yeh arguments ke types ko pattern T a, T b se match karta hai. Dono slots int hain, isliye ek hi candidate hai: T = int.
  2. Instantiate karo. Compiler int maximum(int, int) stamp out karta hai. Kyun? Tabhi real machine code exist karta hai — ek unused template compile hoke kuch nahi banta (Compile-time vs Runtime).
  3. Body evaluate karo. (3 > 7) ? 3 : 77. Kyun? Ternary bada wala pick karta hai.
  4. Doosra call: 2.5, 1.1 dono double hain → T = double, returns (2.5 > 1.1) ? 2.5 : 1.1 = 2.5.

Verify: maximum(3,7) deta hai 7 (ek int); maximum(2.5,1.1) deta hai 2.5 (ek double). Sanity check: answer hamesha do inputs mein se ek hota hai, aur bada wala hota hai. ✓


Ex 2 — Cell B: deduction conflict (degenerate input)

Forecast: Kya yeh 3 return karta hai, 3.0, ya compile karne se mana kar deta hai?

  1. Arg 1 ko pattern se match karo: 3 int hai → T = int. Kyun? Wahi deduction rule, pehle slot par apply kiya.
  2. Arg 2 ko pattern se match karo: 2.5 double hai → T = double. Kyun? Doosra slot bhi T hai, isliye yeh T = double demand karta hai.
  3. Reconcile karo. T ko ek type hona chahiye, lekin step 1 keh raha hai int aur step 2 keh raha hai double. Contradiction → compile error (runtime error nahi). Yeh step kyun? Template argument deduction kabhi conversions insert nahi karta; use har us slot mein exact type match chahiye jo T use karta hai.
  4. Fix — ek T force karo: maximum<double>(3, 2.5). Ab T = double fix hai, aur 3 andar jaate waqt 3.0 mein convert hota hai. Returns 3.0. Conversion ab kyun allowed hai? Jab T haath se pin ho jata hai, toh args ek known double parameter mein pass ho rahe hain — ordinary implicit conversion apply hoti hai.

Verify: maximum(3, 2.5)compile nahi hota. maximum<double>(3, 2.5)3.0, ek double. Sanity: 3.0 > 2.5, isliye bada wala, 3.0, return hota hai. ✓


Ex 3 — Cell C: explicit type argument jo answer badal deta hai

Forecast: Kya dono 3.5 hain? Ya ek truncate hota hai?

  1. half(7): deduce karo T = int (arg int hai). Kyun? Clean single-type deduction (Cell A machinery).
  2. T = int ke saath body: 7 / 2 integer division hai → 3. Yeh step kyun? Body mein likha 2 ek int hai; C++ mein int / int remainder discard karta hai. Template ne arithmetic rules nahi badle — usne sirf int choose kiya.
  3. half<double>(7): T force karke double; 7 convert hota hai 7.0 mein. Kyun? Explicit <double> deduction override karta hai, bilkul Ex 2 ki fix ki tarah.
  4. T = double ke saath body: 7.0 / 23.5. Kyun? double / int mein 2 promote hota hai 2.0 mein, isliye real division hoti hai.

Verify: half(7) == 3 (integer division), half<double>(7) == 3.5. Dono alag hain chosen T ki wajah se, algorithm ki wajah se nahi. ✓


Ex 4 — Cell D: class template ko explicit <T> chahiye

Forecast: Kya constructor ka 42 compiler ko T = int batata hai jaise maximum(42) batata?

  1. Fark yaad karo: function templates T deduce karte hain arguments se; class templates (C++17 se pehle) constructor call se deduce nahi karte. Kyun? Function wahi cheez hai jo tum call karte ho, isliye uske args call par visible hote hain. Class ka naam Box kisi bhi constructor consider hone se pehle ek complete type hona chahiye — pehle type known hona chahiye. Isliye naam lena padta hai: Box<int>.
  2. Box<int> b(42); likho. Compiler ek concrete class Box<int> generate karta hai jahan har T int ban jaata hai. Kyun? Instantiation parameter ko puri class body mein replace karta hai.
  3. b.get() call karo: stored value return karta hai, jo hai 42.
  4. Type identity note karo: Box<int> aur Box<double> unrelated types hain — tum ek ko doosre mein assign nahi kar sakte. Yahi rule hai jo vector<int> aur vector<double> ko alag banata hai (STL Containers).

Verify: Box<int> b(42); b.get()42. Box b(42); pre-C++17 compile nahi hota. ✓


Ex 5 — Cell E: non-type template parameter

Forecast: Kya N ek aisi variable hai jo runtime par change ho sakti hai, ya yeh baked in hai?

Figure — Templates — function templates, class templates
  1. Parameters identify karo: T ek type hai, N ek value hai — specifically ek compile-time int (Compile-time vs Runtime). Yeh step kyun? Templates do flavours ke parameters accept karte hain. N ek type nahi hai, isliye yeh int N slot mein ek actual number fill karta hai. Figure dekho: violet <double, 5> cutter ke dono slots fill karta hai — ek type se, ek number se.
  2. Array<double, 5> instantiate karo. Ab data literally double data[5] hai — ek fixed-size array, size program run hone se pehle known hai. Kyun matter karta hai? Kyunki 5 ek compile-time constant hai, array stack par rehta hai, koi heap allocation nahi, aur compiler sab kuch inline kar sakta hai.
  3. a.size() call karo: N return karta hai, jise compiler ne pehle hi 5 se replace kar diya. Yeh ek compile-time constant hai, effectively return 5;.
  4. Degenerate check — kya N 0 ho sakta hai? Array<double, 0> legal-ish hai lekin zero-length array ek corner case hai (technically ill-formed for plain T data[0]); isse avoid karo. Yeh hamare matrix ka "zero input" cell hai.

Verify: a.size() == 5, storage double[5] stack par hai (5 * 8 = 40 bytes ek typical 8-byte-double platform par). ✓


Ex 6 — Cell F: full specialization (ek type ke liye generic recipe galat hai)

Forecast: Kya dono "1" print karte hain? Ya bool alag behave karta hai?

Figure — Templates — function templates, class templates
  1. Printer<int>::show(1) generic template use karta hai: to_string(1)"1". Yeh step kyun? int ke liye koi special version nahi hai, isliye compiler general cookie-cutter par fall back karta hai.
  2. Printer<bool>::show(true) — compiler exact-match specialization Printer<bool> dekhta hai aur usse generic se prefer karta hai. Kyun? Sabse specialized matching template jeet ta hai (Template Specialization). Figure mein, bool orange arrow follow karke apni dedicated recipe tak jaata hai.
  3. Specialized body run karo: true ? "true" : "false""true". Kyun specialize kiya? Generic to_string(true) deta "1" — technically correct lekin woh human-readable word nahi jo hum chahte the. Specialization us ek type ko fix karta hai jo misbehave karta hai baaki ko touch kiye bina.

Verify: Printer<int>::show(1) == "1", Printer<bool>::show(true) == "true", Printer<bool>::show(false) == "false". ✓


Ex 7 — Cell G: the linker trap (real-world word problem)

Forecast: Kya yeh syntax bug hai, runtime crash hai, ya linker problem hai? Padhne se pehle guess karo.

  1. Stack<int> pehle use kahan hota hai? main.cpp mein. Yeh step kyun? Instantiation use ke point par hoti hai. main.cpp compile karne wala compiler hi hai jise Stack<int>::push generate karna hai.
  2. main.cpp compiler kya dekh sakta hai? Sirf stack.h (declaration). push ki body stack.cpp mein rehti hai, ek separate translation unit (Compile-time vs Runtime). Kyun matter karta hai? Body ke bina, compiler Stack<int>::push ke liye ek call emit karta hai lekin function kabhi generate nahi karta — yeh assume karta hai koi aur unit karega.
  3. stack.cpp kabhi Stack<int> instantiate nahi karta (yeh kabhi int mention nahi karta), isliye koi code generate nahi karta → linker ek dangling reference dhundtha hai → undefined reference. "linker" kyun, "compiler" kyun nahi? Har file apne aap theek compile hui; mismatch tabhi dikhta hai jab object files together link ho rahe hote hain.
  4. Cure: full template definition header (.h/.hpp) mein daalo, taaki har file jo template use kare body dekhe aur instantiate kar sake. Multiple-definition errors se safe kyun hai? Template instantiations ko special linkage milta hai — alag units mein identical instantiations merge ho jaate hain, duplicate nahi hote.

Verify (conceptual): body stack.h mein move karo → main.cpp dekh leta hai → Stack<int>::push generate hoti hai → link succeed karta hai. Bug ki class ek linker error hai, jo undefined reference wording se confirm hoti hai. ✓


Ex 8 — Cell H: exam twist — overload vs template resolution

Forecast: Teen calls, teen answers. Har ek guess karo.

  1. who(5): ek exact non-template match who(int) exist karta hai. Yeh step kyun? Jab ek normal (non-template) function equally good match ho, compiler non-template overload prefer karta hai (Function Overloading). Toh yeh "plain int" return karta hai.
  2. who(2.5): plain who(int) ko double → int conversion chahiye (worse match), lekin who(T) with T = double ek exact match hai. Kyun? Template ek exact type match deta hai; exact, conversion se better hota hai. Returns "template".
  3. who<int>(5): <int> explicitly template ko T = int ke saath demand karta hai, isliye plain overload primary ke taur par consider hi nahi hota — template jeet ta hai. Returns "template". Kyun? Tumne ek template argument name kiya, jo sirf ek template accept kar sakta hai, isliye overload resolution template path par force ho jaati hai.

Verify: who(5) == "plain int", who(2.5) == "template", who<int>(5) == "template". Sanity: 5 ke liye exact-match plain function template ko beat karta hai; 2.5 ke liye template jeetta hai (ek narrowing conversion avoid karta hai) aur jab bhi tum <...> likhte ho. ✓


Recall Quick self-test

maximum(3, 2.5) fail kyun hota hai? ::: Deduction arg 1 se T=int deta hai aur arg 2 se T=double — ek T dono nahi ho sakta. half(7) kya return karta hai? ::: 3, kyunki T=int se 7/2 integer division ho jaata hai. half<double>(7) kya return karta hai? ::: 3.5, kyunki T=double se real division hoti hai. Pre-C++17 mein Box b(42); ko Box<int> b(42); kyun banana padta hai? ::: Class templates T constructor se deduce nahi karte; pehle type name karni padti hai. Printer<bool>::show(true) kya return karta hai? ::: "true", us full specialization se jo generic recipe ko override karti hai. Template ke liye linker error undefined reference ka matlab? ::: Definition ek .cpp mein thi jo using file nahi dekh sakti thi; full body header mein daalo. who(2.5) kya return karta hai? ::: "template" — exact template match, plain overload mein double→int conversion se better hai.