Exercises — Variadic templates — parameter packs, fold expressions
5.2.16 · D4· Coding › C++ Programming › Variadic templates — parameter packs, fold expressions
Shuru karne se pehle, ek picture vo vocabulary pin down karti hai jo hum har problem mein reuse karte hain.

Level 1 — Recognition
L1.1 — Ellipsis padhna
Har line ke liye batao ki ... ek pack declare karta hai ya expand karta hai:
template<typename... Ts>void f(Ts... args)g(args...)(args + ...)
Recall Solution
Hamari reading key: ... naam ke pehle declare karta hai; ... ek pattern ke baad expand karta hai.
typename... Ts—...Tsse pehle hai → declare ek type pack.Ts... args—...argsse pehle hai → declare ek value pack.args...—...patternargske baad aata hai → expand (produce karta haia0, a1, a2, ...).(args + ...)—...fold pattern ke baad aata hai → expand ek fold ke roop mein.
To: declare, declare, expand, expand.
L1.2 — Count vs size
f(1, 'a', 3.14, "hi") ke liye, sizeof...(Ts) kya return karta hai, aur yeh kyun nahi hai sizeof(Ts)?
Recall Solution
4 arguments hain, isliye sizeof...(Ts) . Yeh count karta hai ki pack mein kitne template parameters hain — compile time par known ek constexpr size_t.
sizeof(Ts) illegal hai: Ts types ki poori ek pile hai, ek type nahi, isliye "kitne bytes hai yeh"
ka koi jawab nahi hai. ... sawaal ko bytes se count mein badal deta hai.
Level 2 — Application
L2.1 — Ek right fold evaluate karo
sum(Ts... xs){ return (xs + ...); } diya hua, sum(10, 20, 30, 40) compute karo aur nesting likho.
Recall Solution
Unary right fold right se nest karta hai:
Answer: 100. (Kyunki + associative hai, value ek left fold se match karti, lekin shape
right-nested hai.)
L2.2 — Ek non-associative op ke saath ek left fold evaluate karo
sub(1, 2, 3, 4) compute karo jahan sub left fold (... - xs) use karta hai.
Recall Solution
Unary left fold left se nest karta hai (sabse leftmost op pehle bind karta hai): Answer: -8.
L2.3 — Side effects ke liye comma fold
((std::cout << xs << ' '), ...); print_each('C', '+', '+') ke liye kya print karta hai?
Recall Solution
Comma fold apna pattern har element ke liye left to right ek baar evaluate karta hai, har result discard karta hai.
To yeh cout << 'C' << ' ' run karta hai, phir cout << '+' << ' ', phir cout << '+' << ' '.
Output (note trailing space): C + + .
Hum comma operator precisely isliye use karte hain kyunki hum order mein side effect (printing) ki parwah karte hain,
return values combine karne ki nahi.
Level 3 — Analysis
L3.1 — Empty pack
In mein se kaun compile hote hain jab pack xs empty ho, aur kya value result hoti hai?
(xs + ...)(xs && ...)(xs || ...)(xs, ...)(0 + ... + xs)
Recall Solution
Sirf teen unary folds ke paas ek empty pack ke liye ek defined identity hai; baaki compile errors hain.
(xs + ...)— compile error (unary fold mein+ke liye koi identity element nahi).(xs && ...)— compile hota hai, valuetrue.(xs || ...)— compile hota hai, valuefalse.(xs, ...)— compile hota hai, valuevoid()(kuch nahi).(0 + ... + xs)— compile hota hai, value0. Yeh ek binary fold hai jiskainit = 0missing identity supply karta hai, isliye yeh kisi bhi size ke liye safe hai, zero bhi.
Lesson: ek possibly-empty pack ko safely sum karne ke liye, hamesha ek init do: (0 + ... + xs).
L3.2 — Association shape predict karo
concat ke liye jo + ke saath string-join karta hai, kya (xs + ...) vs (... + xs) result badalta hai?
xs = "a","b","c" (std::string ke roop mein) diya hua, dono outputs do.
Recall Solution
String + produce kiye gaye characters ke liye associative hai, isliye dono "abc" dete hain.
- Right fold
(xs + ...):"a" + ("b" + "c")="a" + "bc"="abc". - Left fold
(... + xs):("a" + "b") + "c"="ab" + "c"="abc". Value identical hai; sirf intermediate temporaries differ karte hain. Isko subtraction se contrast karo jahan value khud badal jaati hai — isliye "kya direction matter karta hai?" operator ki algebra par depend karta hai.
L3.3 — Count karo kitne booleans true hain
count_true(Bs... bs) likho aur trace karo jo count karta hai kitne arguments true hain, ek fold use karke.
count_true(true, false, true, true, false) ke liye evaluate karo.
Recall Solution
Har bool 0 ya 1 mein convert hota hai, isliye ek sum fold trues count karta hai:
template<typename... Bs>
constexpr int count_true(Bs... bs) {
return (0 + ... + static_cast<int>(bs)); // binary left fold, safe if empty
}Trace: . Answer: 3.
Humne binary fold init = 0 ke saath choose kiya taaki empty case (count_true() = 0) abhi bhi compile ho.
Level 4 — Synthesis
L4.1 — min_of scratch se build karo
Ek variadic min_of(a, rest...) likho jo sabse chhota argument return kare. min_of(5, 2, 8, 1, 9) trace karo.
Recall Solution
Ek plain operator ke saath ek fold seedha min nahi karega, lekin hum ek lambda-like helper ke saath fold kar sakte hain,
ya recurse kar sakte hain. Sabse clean fold ek comma-driven update ke zariye std::min use karta hai, lekin sabse clear synthesis
ek helper ke zariye ternary use karke ek running minimum par fold hai. Yahan ek recursive build hai
(parent ke peel-the-head pattern se match karta hai):
template<typename T> T min_of(T a) { return a; } // base: one left
template<typename T, typename... R>
T min_of(T a, R... rest) {
T m = min_of(rest...); // smallest of the tail
return a < m ? a : m; // compare head against it
}Trace min_of(5,2,8,1,9):
min_of(9)= 9min_of(1,9)→ m=9 → 1<9 → 1min_of(8,1,9)→ m=1 → 8<1? no → 1min_of(2,8,1,9)→ m=1 → 2<1? no → 1min_of(5,2,8,1,9)→ m=1 → 5<1? no → 1
Answer: 1. Har call ek head peel karta hai aur rukne ke liye base case min_of(T a) ki zaroorat hoti hai.
L4.2 — Ek fold jo count bhi kare
average(xs...) likho jo arithmetic mean double ke roop mein return kare. average(2, 4, 9) ke liye evaluate karo.
Recall Solution
Ek sum fold ko sizeof... ke saath combine karo:
template<typename... Ts>
double average(Ts... xs) {
return (0.0 + ... + xs) / sizeof...(xs); // safe even if... (see note)
}Trace average(2,4,9): sum ; count ; mean .
Answer: 5.0. (Real code mein divide karne se pehle sizeof...(xs) == 0 ke against guard karo.)
L4.3 — Ek pack ko ek tuple mein forward karo
Dikhao ki sab arguments ko ek std::tuple mein value categories preserve karte hue kaise store karein,
Perfect Forwarding use karke. pack_it(1, "x", 2.5) ke result ka std::tuple_size kya hai?
Recall Solution
template<typename... Ts>
auto pack_it(Ts&&... xs) {
return std::make_tuple(std::forward<Ts>(xs)...); // expand: forward each element
}Pattern std::forward<Ts>(xs) trailing ... dwara har element ke liye ek baar repeat hota hai, isliye har
argument apni lvalue/rvalue nature rakhta hai. pack_it(1, "x", 2.5) 3 elements ka tuple build karta hai, isliye
std::tuple_size<decltype(...)>::value = 3 — sizeof...(Ts) se match karta hai.
Level 5 — Mastery
L5.1 — Custom operator ke saath fold
operator| overloaded diya hua taaki a | b return kare a agar a>b else b (ek "max" pipe), to
(x | ...) (3, 7, 2, 9, 5) ke liye kya compute karta hai? Dekho Operator Overloading.
Recall Solution
Right fold right se nest karta hai, lekin | yahan bada return karta hai, aur "bada" associative hai,
isliye order value nahi badalta:
Answer: 9. Isi tarah tum ek variadic max ek fold line mein paate ho jab ek baar operator comparison encode kar le.
L5.2 — Compile-time fold
constexpr and Compile-time Computation use karke, ek constexpr variadic product likho aur
product(2, 3, 5) evaluate karo — aur explain karo ki multiplication kab hoti hai.
Recall Solution
template<typename... Ts>
constexpr auto product(Ts... xs) { return (1 * ... * xs); } // binary left fold, init=1. Answer: 30. Kyunki function constexpr hai aur arguments
literals hain, compiler poore fold ko compile time par evaluate karta hai — runtime binary mein sirf
constant 30 hota hai. init = 1 bhi product() (empty) ko safely 1 return karta hai.
L5.3 — Mixed fold, exact output predict karo
In ka printed output, order mein, trace karo:
template<typename... Ts>
void dump(Ts... xs) {
std::size_t i = 0;
((std::cout << i++ << ":" << xs << ' '), ...);
}dump("a", 42, 'Z') ke liye.
Recall Solution
Comma fold pattern left→right evaluate karta hai, aur i++ har print ke baad advance karta hai:
- element
"a": print karta hai0:a, phiri1 ho jaata hai - element
42: print karta hai1:42, phiri2 ho jaata hai - element
'Z': print karta hai2:Z, phiri3 ho jaata hai
Output: 0:a 1:42 2:Z . Comma fold ka Left-to-right evaluation guaranteed hai, isliye
index counter consistent rehta hai.
Recall Self-test summary
4 args ke liye sizeof...(Ts) ::: 4
sum(10,20,30,40) (xs + ...) se ::: 100
sub(1,2,3,4) (... - xs) se ::: -8
count_true(T,F,T,T,F) ::: 3
min_of(5,2,8,1,9) ::: 1
average(2,4,9) ::: 5.0
product(2,3,5) (1 * ... * xs) se ::: 30
Empty-pack (xs + ...) ::: compile error; (0 + ... + xs) use karo