5.2.16 · D5 · HinglishC++ Programming

Question bankVariadic templates — parameter packs, fold expressions

2,438 words11 min read↑ Read in English

5.2.16 · D5 · Coding › C++ Programming › Variadic templates — parameter packs, fold expressions

Deeper background ke liye parent dekho: Variadic templates (parent).


0. Pehle symbols ke naam (traps se pehle padho)

Neeche har item mein char chhote naam reuse hote hain. Ye sirf conventional letters hain, keywords nahi — tum inhe rename kar sakte ho. Yahan exactly bataya gaya hai ki har ek kya stand karta hai, ek concrete call ke saath taaki koi symbol mystery na rahe.

Figure — Variadic templates — parameter packs, fold expressions

1. Do pictures jo tumhare dimag mein rehni chahiye

Poora topic ek distinction mein rehta hai: ek pack ko expand karna (uske elements ko ek row mein bichhaana) vs usse fold karna (row ko ek operator se collapse karna, nested ek side ko). Is bank ka har trap actually inhi do shapes mein se ek ke baare mein question hai.

Figure — Variadic templates — parameter packs, fold expressions
Figure — Variadic templates — parameter packs, fold expressions

True or false — justify karo

Ek parameter pack mein zero elements ho sakte hain.
True. Ek pack ka matlab hai "zero ya zyada" — empty pack ke saath f() legal hai, aur yahi reason hai ki recursive expansion ko ise catch karne ke liye ek base-case overload chahiye.
sizeof...(Ts) run time par evaluate hota hai.
False. Ye ek constexpr size_t hai jo compile time par known hota hai; tum ise if constexpr, array sizes, aur dusre compile-time contexts mein use kar sakte ho.
(xs + ...) aur (... + xs) hamesha same numeric result dete hain.
+ ke liye True kyunki wo associative hai, lekin association order phir bhi alag hota hai (right-nested vs left-nested); non-associative operators jaise - ya / ke liye results alag hote hain.
(xs + ...) mein parentheses optional stylistic choices hain.
False. Ye fold syntax ka mandatory part hain — grammar rule hai ( pack op ... ), toh inhe hatane se parse error aata hai, warning nahi.
Empty pack par * ke saath unary fold 1 deta hai.
False. Sirf &&, ||, aur , ke defined empty-pack identities hain; empty pack par * compile error hai, implicit 1 nahi.
typename... Ts aur Ts... args dono packs declare karte hain.
True. Pehla ek type pack declare karta hai, doosra ek value (function-argument) pack; naam se pehle ... ka matlab hamesha "ek pack declare karo" hota hai.
f(args)... aur f(args...) same hain.
False. f(args)... expand hota hai f(a0), f(a1), f(a2) mein (f ko har ek par call karo), jabki f(args...) ek single call hai f(a0, a1, a2).
Ek variadic function template pack se pehle normal (non-pack) parameters bhi le sakta hai.
True. Jaise void log(const char* tag, Ts... rest). Pack sirf fixed parameters ke baad ki sab cheez absorb karta hai.
Fold expressions recursive pack processing ko completely replace karte hain.
False. Folds "ek binary operator ke saath combine karo" wale cases handle karte hain; jab har element ko type ya position ke basis par alag logic chahiye, recursion (ya if constexpr) phir bhi tool hai.
(cout << xs << ' ', ...) left-to-right printing order guarantee karta hai.
True. Ek fold mein comma operator evaluations ko left→right sequence karta hai, toh side effects (printing) argument order mein hote hain.

Error dhundo

return xs + ...; — kya galat hai, aur compiler kya kehta hai?
Mandatory fold parentheses missing hain; likho return (xs + ...);. Unke bina GCC/Clang roughly error: expected primary-expression before '...' token report karte hain kyunki xs + ... ek recognised fold nahi hai.
bool ok = (bs && ...); jab bs empty ho sakta hai — kya ye safe hai?
Safe. && un teen operators mein se ek hai jinke paas empty-pack identity hai (true), toh ek empty pack error ki jagah true return karta hai.
auto s = (xs * ...); jahan xs empty ho sakta hai — diagnostic kaisi dikhti hai?
Empty pack par compile error: error: fold of empty expansion over operator* (Clang) — * ka koi fold identity nahi hai. Binary fold aur init se fix karo: (1 * ... * xs).
void f(Ts... args) { g(args); } — kyun compile nahi hoga?
args ek pack hai, single value nahi; diagnostic hai error: parameter packs not expanded with '...'. Tumhe ise expand karna hoga: g(args...). Bare args sizeof... ke andar ke alawa illegal hai.
Pack elements count karne ke liye size_t n = sizeof(Ts); — error?
Haan: error: parameter packs not expanded with '...'. Pack par sizeof(Ts) ill-formed hai; count operator hai sizeof...(Ts). ... meaning ko entirely badal deta hai.
template<typename... Ts>
void print(Ts... rest) { std::cout << ...; }   // ill-formed
Ye kyun broken hai?
std::cout << ... ke fold ke operand side par koi pack expression nahi hai, toh compiler kuch aisa emit karta hai error: expected expression before '...'. Tumhe chahiye ((std::cout << rest << ' '), ...); (ek comma fold) ya ek recursive head/tail split.
template<typename T, typename... Rest>
void print(T first, Rest... rest) { std::cout << first; print(rest...); }
// no  void print() {}  overload
Jab pack empty ho jata hai toh kya hota hai?
Final print() call ko koi matching overload nahi milta; compiler fail karta hai error: no matching function for call to 'print()' ke saath — recursion ka koi terminator nahi hai.
auto avg(Ts... xs) { return (xs + ...) / sizeof...(xs); } — subtle bug?
Empty pack par (xs + ...) compile fail ho jata hai (fold of empty expansion over operator+); agar compile bhi hota, sizeof... = 0 se divide karna undefined hai. sizeof...(xs) unsigned hona bhi signed average ko skew kar sakta hai.

Why questions

Variadic templates kyun exist karte hain 1..N args ke liye manually overloading ki jagah?
Hand-overloading repetitive hai aur arbitrary cap rakhta hai; ek pack "kisi bhi types ke 1 se N arguments" ko ek reusable, type-safe pattern mein fold karta hai jise compiler demand par instantiate karta hai.
... ko expansion ke liye pattern ke baad kyun place kiya jata hai lekin declaration ke liye naam se pehle?
Ye ek positional convention hai: leading ... ek pack banata hai, trailing ... apne left wale pattern ko har pack element ke liye ek baar repeat karta hai (f(args)...f(a0), f(a1), ...).
- ke liye fold direction (left vs right) matter kyun karta hai lekin + ke liye nahi?
- non-associative hai, toh 1-(2-3)=2 alag hai (1-2)-3=-4 se; + associative hai toh sare groupings same value dete hain — sirf nesting shape badlti hai.
print_each ke liye recursion ki jagah comma fold prefer kyun karein?
Comma fold ek line hai, koi base case nahi chahiye, aur left-to-right side effects guarantee karta hai — recursion yahan same idea ke liye do functions banata.
sizeof... compile-time constant kyun hai jabki recursive count run-time hoti?
Pack ka size template instantiate hote hi fix ho jata hai, toh compiler literal count directly code mein substitute karta hai — kuch bhi program run hone par compute nahi hota.
Kaise ek fold Perfect Forwarding ke saath pair karta hai — forwarding actually kya preserve karta hai?
f(std::forward<Ts>(args)...) mein ... std::forward<Ts>(arg) ko har element ke liye ek baar repeat karta hai. Har std::forward<Ts> Ts&& mein wapas cast karta hai, toh ek argument jo rvalue ke roop mein aaya tha wo rvalue (movable) rehta hai aur lvalue, lvalue rehta hai — pack expansion wo value-category-preserving cast har element par uniformly apply karta hai.
Kyun std::tuple construction pack expansion se itni naturally nikalta hai?
Ek tuple ek ordered heterogeneous list hai, aur std::make_tuple(args...) pack ko exactly usi comma-separated argument list mein expand karta hai — pack ki "har slot mein ek type/value" structure tuple ke fields ki same shape hai, toh koi glue code nahi chahiye.
if constexpr aksar type ke basis par packs process karte waqt recursion ko kyun replace karta hai?
Ye ek function ko compile time par sizeof... ya type traits par branch karne deta hai, aur untaken branch ko discard karta hai, toh terminate karne ke liye alag base/recursive overloads likhne ki zaroorat nahi.

Edge cases

Single-element pack (x + ...) ka unary right fold kya reduce karta hai?
Sirf x — ek element ke saath combine karne ke liye kuch nahi, toh fold wo element unchanged return karta hai.
Single-element pack (... + x) ka unary left fold kya reduce karta hai?
Bhi sirf x. Left aur right folds sirf 2+ elements ke saath distinguishable hain; single element par dono lone value tak collapse hote hain, toh (... + x) aur (x + ...) yahan identical hain.
Empty pack par (... , args) (comma fold) kya evaluate karta hai?
void() — comma un teen operators mein se ek hai jinke paas defined empty-pack identity hai, toh ye valid no-op hai.
Kya ek template ke same parameter list mein do parameter packs ho sakte hain?
Generally function templates ke liye nahi — sirf last parameter ek pack ho sakta hai, kyunki compiler do greedy packs ke beech arguments split nahi kar sakta (partial specializations ek narrow exception hain).
Agar tum ek variadic template ko zero arguments ke saath call karo toh kya hota hai?
Valid hai; sizeof... 0 hai, unary folds ko teen empty-safe operators mein se ek use karna hoga ya binary init par switch karna hoga, aur recursive versions immediately base case hit karte hain.
Kya (0 + ... + xs) result change karta hai jab xs non-empty ho?
+ ke liye koi meaningful change nahi: ye sirf left-fold ko 0 se seed karta hai, deta hai ((0+a0)+a1)+...; safety sirf tab matter karti hai jab xs empty ho sakta ho.
Binary right fold (xs + ... + init) ke liye, init nesting mein kahan baithta hai?
Sabse gehri right position par: a0 + (a1 + (... + (an + init)))init right se last mein combine hota hai, empty pack ke liye bhi ek value guarantee karta hai.
Kya (xs op ...) legal hai jab elements ke mixed types hon?
Haan, jab tak op successive operand types ke beech defined ho (built-ins ya Operator Overloading ke zariye); result type usual promotion/overload-resolution rules follow karta hai.

Recall Interactive self-test (hints included — khud check karo)

Har ek ko zyoor se jawab do, phir upar ke sections ki apni memory expand karke hint reveal karo:

  1. Teen operators ke naam batao jinke unary fold empty pack par legal hain, aur har ek ki identity do. Hint: ye short-circuit boolean pair plus sequencing operator hain → &&true, ||false, ,void().
  2. Ek sentence mein batao kyun fold mein parentheses mandatory hain. Hint: grammar production socho, style nahi → ( pack op ... ) hi rule hai, toh inhe hatana = parse error.
  3. Explain karo kyun (xs - ...)(... - xs). Hint: nesting draw karo → right = 1-(2-3)=2, left = (1-2)-3=-4; - non-associative hai.

Agar tumhare kisi bhi jawab mein koi reason attached nahi tha sirf bare fact ke, toh wo item pehle revisit karne wala hai.