5.2.14 · D4 · HinglishC++ Programming

ExercisesTemplates — function templates, class templates

3,026 words14 min read↑ Read in English

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: T use 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) aur twice(4) dono T = int deduce karte hain → ek int version, shared.
  • twice(2.5) T = double deduce karta hai → doosra version.
  • twice<long>(9) T = long force 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 T deduce nahi kar sakta. Tumhe Box<int> b(5); likhna hoga. (C++17+ mein Class Template Argument Deduction Box 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 = double

Fix 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) N 5 ke roop mein baked in hai, toh a.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. Toh sizeof(a) == 40. N ka 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 ka int full 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')char ke liye koi special version nahi; primary template T = char ke 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 hai T = int ke 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 — kyunki i++ 5 return karta hai jo > 3 hai — true branch i++ phir se run karta hai. Toh i do baar increment hota hai. Yahi classic macro trap hai "double evaluation."
  • (b) Start i = 5. Condition i++: 5 use karta hai, i 6 ban jaata hai. Kyunki 5 bada hai 3 se, true branch i++ run karta hai: 6 use karta hai, i 7 ban jaata hai. Final i == 7 (aur j == 6).
  • (c) Ek template ek real function hai, text substitution nahi. Uska argument a parameter 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 expression i++ phir kabhi run nahi hota. Toh maxT(i++, 3) i ko 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.