Exercises — Templates — function templates, class templates
5.2.14 · D4· Coding › C++ Programming › Templates — function templates, class templates
Jo bhi answer ek number produce karta hai, woh machine-verified hai.
Level 1 — Recognition (kya tum syntax padh sakte ho?)
L1·Q1 — Template head dhundho
Recall Solution
Answer: B.
Keyword template hota hai, uske baad angle-bracket list of parameters, aur har type parameter ko typename (ya equivalent class) se introduce karna zaroori hai. Isliye template<typename T> (ya template<class T>).
- A galat hai:
Tuse ho raha hai declare hone se pehle — tum<T>nahi likh sakte, tumhe kehna hoga "T ek type hai":<typename T>. - C aur D sirf keywords ko ulta-seedha kar dete hain.
L1·Q2 — Instantiations count karo
Recall Solution
Answer: 3. Instantiation concrete type se keyed hoti hai, na ki tumne kitni baar call kiya usse.
twice(3)aurtwice(4)donoT = intdeduce karte hain → ekintversion, shared.twice(2.5)T = doublededuce karta hai → doosra version.twice<long>(9)T = longforce karta hai → teesra version. Same type = same generated function reuse hoti hai. Toh do int-calls ek function mein collapse ho jaate hain, aur double aur long versions milakar teen distinct functions bante hain.
L1·Q3 — Kaunse ko explicit <...> chahiye?
Recall Solution
- (i) compile hota hai. Tumne type explicitly diya:
Box<int>. - (ii) C++14 mein compile NAHI hota. Ek class template C++17 se pehle apne constructor se
Tdeduce nahi kar sakta. TumheBox<int> b(5);likhna hoga. (C++17+ mein Class Template Argument DeductionBox b(5)ko legal bana deta hai, lekin question C++14 pin karta hai.)
Level 2 — Application (ise kaam par lagao)
L2·Q4 — Deduction conflict fix karo
Recall Solution
Failure: 3 se deduction kehta hai T = int, 2.5 se kehta hai T = double. Ek T, do demands → conflict.
Fix A — type explicitly force karo:
auto r = maximum<double>(3, 2.5); // 3 convert hoke 3.0 ban jaata hai, T = doubleFix B — do type parameters:
template<typename T1, typename T2>
auto maximum(T1 a, T2 b){ return (a > b) ? a : b; }
auto r = maximum(3, 2.5);r ki value: 3 aur 2.5 compare karo; 3 bada hai 2.5 se, toh result 3 hai (as a double, yaani 3.0).
L2·Q5 — Non-type parameter size
Recall Solution
- (a)
N5ke roop mein baked in hai, toha.size()5 return karta hai. - (b) Sirf ek member hai
double data[5], yaani 5 doubles at 8 bytes each: 5 times 8 equals 40 bytes. Tohsizeof(a) == 40.Nka compile-time value hona (ek non-type parameter, upar define kiya hua) exactly isliye hai kyunki array stack par ek fixed, known size ke saath rehta hai — dekho Compile-time vs Runtime.
L2·Q6 — Ek generic swap likho
Recall Solution
template<typename T>
void swapValues(T& a, T& b){ // references, taaki caller ke variables change hon
T tmp = a; // a ko stash karo
a = b; // a ban jaata hai b
b = tmp; // b ban jaata hai old a
}T& kyun? References ke bina hum copies swap karte aur caller ko kuch nazar nahi aata. & real variables se bind karta hai.
Trace: start x=1, y=9. tmp=1; x=9; y=1. Final: x == 9, y == 1.
Level 3 — Analysis (yeh aisa kyun behave karta hai?)
L3·Q7 — Linker error diagnosis
Recall Solution
Kyun fail hota hai: Ek template code nahi hota — yeh ek recipe hai. Code sirf point of use par generate hota hai. Point of use main.cpp mein hai. Lekin main.cpp sirf math.h se declaration dekhta hai; body math.cpp mein rehti hai, jo ek alag translation unit hai jise main.cpp ka compiler kabhi nahi padhta. Body ke bina, compiler square<int> instantiate nahi kar sakta, toh woh ek aise function ka call emit karta hai jo kabhi define nahi hoga → linker error.
Fix: Full definition ko header (math.h) mein rakho. Phir har file jo ise include karti hai woh poori body dekhti hai aur instantiate kar sakti hai.
Value: fix hone ke baad, square(4) 4 times 4 return karta hai, jo 16 hai.
L3·Q8 — Kaun sa overload / specialization jeetta hai?
Recall Solution
Rule of thumb: non-template exact match ek template ko beat karta hai; templates mein sabse zyaada specialized wala jeetta hai.
- (i)
pick(5)→ template kaintfull specialization exist karta hai aur exactly match karta hai →"int-special". - (ii)
pick(2.0)→ ek ordinary (non-template)pick(double)hai. Jab dono equally well match karein toh non-template function kisi bhi template se prefer hota hai →"plain-double". - (iii)
pick('a')→charke liye koi special version nahi; primary templateT = charke saath instantiate hota hai →"generic". Full ranking rules ke liye dekho Template Specialization aur Function Overloading.
Level 4 — Synthesis (kuch naya banao)
L4·Q9 — Ek generic Pair with a swap
Recall Solution
#include <type_traits>
template<typename A, typename B>
class Pair {
public:
A first;
B second;
Pair(A a, B b) : first(a), second(b) {}
void flip() {
// hard compile-time guard: refuse unless A and B are the SAME type
static_assert(std::is_same<A, B>::value,
"flip() requires both members to have the same type");
A tmp = first;
first = second;
second = tmp;
}
};static_assert(std::is_same<A,B>::value, ...) kyun? Iske bina, flip() kisi bhi do convertible types ke liye silently compile ho jaata (jaise int aur short), jo hum nahi chahte. static_assert compiler ko Pair<int,short>::flip() ek clear message ke saath reject karne par majboor karta hai, taaki sirf genuinely same-type pairs kaam kar sakein.
Trace: Pair<int,int> p{3,8} → first=3, second=8. flip(): tmp=3; first=8; second=3. Result: p.first == 8, p.second == 3.
L4·Q10 — Pointer-printing Printer ke liye specialize karo
Recall Solution
template<typename T>
struct Printer<T*> { // partial specialization: koi bhi pointer
static const char* kind(){ return "pointer"; }
};Pattern Printer<T*> kisi bhi pointer type se match karta hai aur primary template se zyaada specialized hai, toh compiler ise tab prefer karta hai jab argument ek pointer ho.
Printer<int>::kind()→ koi*nahi, primary template →"value".Printer<int*>::kind()→T*se match karta haiT = intke saath →"pointer".
Level 5 — Mastery (sab kuch saath lagao)
L5·Q11 — Generic fixed-size stack
Recall Solution
template<typename T, int N>
class Stack {
T data[N]; // fixed storage, size compile time par pata hai
int top = 0; // abhi kitne elements hain
public:
void push(T v){ data[top++] = v; }
T pop(){ return data[--top]; }
int count() const { return top; }
};top trace karo: start 0. push, push, push → 3. pop → 2. push, push → 4.
Toh s.count() == 4. (Capacity N=4 ek non-type parameter hai jo compile time par baked in hai; buffer stack par rehta hai.)
L5·Q12 — Macro vs template safety compare karo
Recall Solution
- (a) Text substitution se milta hai:
int j = ((i++) > (3) ? (i++) : (3));i++do baar aata hai. Yeh condition mein ek baar evaluate hota hai, aur — kyunkii++5return karta hai jo> 3hai — true branchi++phir se run karta hai. Tohido baar increment hota hai. Yahi classic macro trap hai "double evaluation." - (b) Start
i = 5. Conditioni++: 5 use karta hai,i6 ban jaata hai. Kyunki 5 bada hai 3 se, true branchi++run karta hai: 6 use karta hai,i7 ban jaata hai. Finali == 7(aurj == 6). - (c) Ek template ek real function hai, text substitution nahi. Uska argument
aparameter initialize karne ke liye exactly ek baar evaluate hota hai, aur phir sirf woh stored parameter (ek copy) compare aur return hoti hai — caller ka expressioni++phir kabhi run nahi hota. TohmaxT(i++, 3)iko sirf ek baar increment karta hai. Yeh single-evaluation guarantee, full type-checking ke saath, woh core reason hai kyun templates macros ko beat karte hain. Dekho Macros vs Templates.
Recall Ek-line self-check
Templates function arguments se type deduce karte hain lekin C++17 se pehle classes ke liye explicitly spell out karna padta hai ::: Sahi — function argument deduction vs no constructor deduction. Ek macro apne arguments ko double-evaluate karta hai; ek template har argument ko ek baar evaluate karta hai ::: Sahi — templates real functions hain. Non-type template parameters compile-time constants hone chahiye ::: Sahi.