Visual walkthrough — Concepts (C++20) — constraining templates
5.2.18 · D2· Coding › C++ Programming › Concepts (C++20) — constraining templates
Yeh page Concepts (C++20) — constraining templates aur uske prerequisites par build karta hai: Templates — function & class templates, SFINAE and std::enable_if, type_traits — std::integral, std::floating_point, aur Overload Resolution & Subsumption.
Step 1 — Template asal mein kya hai: ek machine jisme ek blank slot hai
KYA HAI. template<typename T> T sq(T x){ return x*x; } ko ek rubber stamp ki tarah socho jisme type ke liye ek jagah khaali hai.
KYUN. Hum chahte hain ek recipe jo int, double, float, ... sab ke liye kaam kare — bajaaye ek hi function ko das baar likhne ke. Khaali slot T hi poora point hai.
TASVEER. Figure mein, baayein haath ka stamp ek khaali grey socket rakhta hai jisme T likha hai. Ek type (int) us socket mein girti hai, aur stamp ek concrete function int sq(int x) press karke nikalata hai.

Khatre ki baat: stamp koi bhi type socket mein daalne par accept kar lega — chahe std::string hi kyun na ho, jisme koi meaningful * nahi hota. Koi bhi buri type ko darwaze par rokta nahi. Yahi missing darwaza hai jise concepts solve karte hain.
Step 2 — Puraana failure: type andar ghus jaati hai, phir andar hi blast hoti hai
KYA HAI. Type pehle admit hoti hai; check baad mein, body ke andar gehraai mein hoti hai.
KYUN DARD DETA HAI. Compiler ki ungali return x*x; par point karti hai — recipe ke andar ek line — na ki tumhari call sq(myString) par. Yahi woh "200-line template error" hai jiska parent note ne warning diya tha.
TASVEER. String khule socket se nikal jaati hai (green arrow), stamp ho jaati hai, aur x*x par ek laal dhamaka hota hai — call site se kaafi door. Error ki jagah (laal star) uss jagah ke paas bilkul nahi hai jahan tumse galti hui thi (grey call site).

Hum chahte hain woh laal star wapas front door par aaye. Yahi Step 3 hai.
Step 3 — Concept ek bouncer hai: type par compile-time yes/no
KYA HAI. Ek concept hamesha apne template head ke neeche introduce hota hai, taaki placeholder type ka naam pehle se ho:
Pehli line T declare karti hai; tabhi T body mein use ho sakta hai. || seedha "OR" hai: agar dono mein se koi ek hold kare toh pass.
KYUN yeh tool aur koi if nahi? Ek if runtime par chalta hai, jab type kabka ja chuka hota hai — type sirf compilation ke dauran exist karti hai. Humein ek aisa check chahiye jo compile time par rahe. std::integral aur std::floating_point (type_traits — std::integral, std::floating_point se) exactly aise hi compile-time sawaal hain, isliye hum unse apna concept banate hain.
TASVEER. Socket par ab ek bouncer hai. int aata hai, bouncer Numeric<int> = true evaluate karta hai, rope khulta hai. std::string aata hai, Numeric<string> = false, rope band rehta hai aur string darwaze par hi wapas ho jaati hai — andar koi dhamaka nahi.

Step 4 — Bouncer ko lagaana: chaar darwaaze, ek hi guard
KYA HAI. Neeche charon ek hi guarded function sq stamp karte hain:
- Abbreviated parameter — guard parameter par hi sawaar hai:
- Constrained template parameter — guard type list mein
typenameki jagah leta hai: - Leading requires-clause — guard template head ke baad ek alag clause hai:
- Trailing requires-clause — guard parameter list ke baad, body se pehle aata hai:
KYUN chaar? Ergonomics ke liye. Form 1 one-liners ke liye sabse chota hai; form 2 ek single named concept ke saath sabse saaf dikhta hai; forms 3 aur 4 tab kaam aate hain jab condition ek complicated ad-hoc boolean ho jise naam dena zaroori na lage. Ek hi bouncer, chaar alag doorframes.
TASVEER. Chaar doorframes side by side bane hain, har ek mein wahi bouncer figure khadi hai, har ek int ko andar aane deti hai aur string ko rokti hai. Tasveer ka point: doorframe badalna kabhi nahi badlta ke kise andar aane milta hai.

Step 5 — Bouncer haath se banana: requires-expression
KYA HAI. "Aisi types jo add ho sakein aur jinका sum phir bhi T rahe" banane ke liye:
T a, T bkaalfarz hain — kabhi actually construct nahi hote. Woh sirf isliye hain taake huma+blikh sakein aur compiler se poochh sakein "kya yeh line compile hogi?"{ a + b }ek compound requirement hai: expression valid hona chahiye.-> std::same_as<T>abhi-defined concept use karke result ka type exactlyTpin karta hai.
KYUN requires-expression aur normal function nahi? Ek normal function a+b run karne ki koshish karta aur ek number compute karta. Humein number nahi chahiye — humein yes/no chahiye ke "kya yeh compile bhi hota hai?". Requires-expression woh akaela tool hai jo compilability ko khud ek bool ke roop mein return karta hai.
TASVEER. Ek checklist clipboard. Har requirement line par ek checkbox hai. Bouncer tab hi ek box tick karta hai jab woh line compile hoti hai. Saare boxes ticked → poora expression true (green). Ek box fail → false (red). Clipboard par kuch bhi kabhi execute nahi hota.

Step 6 — Overloading: do bouncers, aur silent turn-away
KYA HAI. Diya gaya
call describe(5) (int ke saath):
- (A) ka bouncer:
integral<int> = true→ candidate set mein rehta hai. - (B) ka bouncer:
floating_point<int> = false→ remove, bina awaaz ke.
Ek candidate bachta hai → woh choose ho jaata hai. Yeh Overload Resolution & Subsumption in action hai, koi SFINAE and std::enable_if ki gymnastics nahi chahiye.
TASVEER. Do darwaaze A aur B. Value 5 aata hai; darwaza A green hoke khulta hai, darwaza B grey hokar band ho jaata hai (removed, koi alarm nahi). Neeche, value 3.14 mirror karta hai: B khulta hai, A grey ho jaata hai.

Step 7 — Edge & degenerate cases: woh corners jo tumhe kabhi touch nahi karne chahiye
Case α — ek type DONO bouncers satisfy karti hai, aur ek doosre ko subsume karta hai.
Maano SignedIntegral<T> Integral<T> ke roop mein define hai aur aur zyada. Toh SignedIntegral Integral ko subsume karta hai: woh strictly zyada demand karta hai. Agar koi type dono pass kare, compiler zyada constrained darwaaze ko prefer karta hai. Yahi wajah hai ke concepts ordered specialisation dete hain.
Case β — dono pass hote hain lekin NA HI ek doosre ko subsume karta hai (ambiguity).
Ab socho do overloads hain jo alag-alag concepts se guarded hain, maano Hashable<T> aur Serializable<T>, aur ek type T jo dono satisfy karti hai. Na hi koi concept strictly doosre se zyada demand karta hai — woh incomparable hain, jaise do overlapping circles bina nesting ke. Compiler koi "zyada constrained" winner nahi chun sakta, toh call ambiguous ho jaati hai aur tumhe ek hard "call to ... is ambiguous" error milta hai. Fix: ek teesra overload add karo jo Hashable<T> && Serializable<T> se constrained ho (jo dono ko subsume karta hai), ya ek requirement ko strictly doosre ko imply karo.
Case γ — ek type KISI BHAI bouncer ko satisfy nahi karti.
describe("hi") ek const char* ke saath: dono integral aur floating_point false hain. Dono candidates removed → zero survivors → compiler saaf message deta hai "no matching function for call to describe" — tumhare call site par pointed — exactly woh jeet jo hum chahte the.
Case δ — khali/degenerate requires-expression.
Ek requires-expression jisme koi requirements nahi hain, requires{ }, vacuously true hoti hai (ek khaali conjunction). Aur ek deleted operator wali type ka fictional variable uss ek line ko fail kar deta hai — poore expression ko false kar deta hai — baaki lines ko touch kiye bina.
TASVEER. Chaar mini-panels: (α) do nested rings, andar wali "SignedIntegral" ring winner ke roop mein highlighted; (β) do overlapping lekin un-nested circles shared middle mein laal "ambiguous" mark ke saath; (γ) dono darwaaze band ek saaf signpost ke saath "no matching function"; (δ) ek khaali checklist green glow karte hue (vacuously true) ek aisi checklist ke saath jisme ek laal box poore result ko laal kar raha hai.

Ek-tasveer summary
Sab kuch ek pipeline mein compress ho jaata hai: ek type body stamp hone se pehle ek compile-time bouncer se milti hai; pass hone wali types ek saaf stamped function mein flow karti hain, fail hone waali types darwaze par hi wapas ho jaati hain call-site message ke saath; jab kai darwaaze apply hote hain toh sabse zyada constrained wala jeetta hai — jab tak unme se koi doosre se zyada constrained na ho, us case mein call ambiguous ho jaati hai.

Recall Feynman: poora walkthrough seedhe shabdon mein
Ek template ek rubber stamp hai jisme ek khaali socket hai — type daalo, asli code nikalega. Puraane C++ mein socket mein koi bhi type ghus sakti thi aur tab hi chillata tha jab stamped code kuch aisa karne ki koshish karta jo type kar hi nahi sakti thi (jaise string ko multiply karna) — aur recipe ke andar se chillata tha, tumhari galti se kaafi door. Ek concept ek bouncer hai jo hum socket par rakhte hain: type ke baare mein ek compile-time yes/no sawaal, jaise "kya yeh ek number hai?" Hum bouncer ya toh ready-made sawaalon se banate hain (std::integral || std::floating_point) ya haath se ek requires-expression se — ek clipboard jisme lines hain jinhe compiler compile karne ki koshish karta hai (kabhi run nahi karta); saari lines compile → true, ek fail → false. Tum is bouncer ko chaar alag tarakon se lagaa sakte ho; sab ka matlab ek hi hota hai. Jab tumhare paas kai functions hain jinke apne bouncers hain, toh ek call unhe saari ko dikhaayi jaati hai; jinkaa bouncer "nahi" kehta unhe quietly remove kar diya jaata hai (yeh error nahi hota), aur agar do "haan" kehte hain toh zyada kathor wala jeetta hai — lekin agar koi bhi zyada kathor nahi hota, toh call ambiguous hoti hai aur fail ho jaati hai. Sirf tab jab koi bhi nahi bachta tum "no matching function" error paate ho — aur woh saaf wala hota hai, seedha tumhare call par pointing karta hua.
Recall Quick self-test
Har line mein ::: marker ek prompt (baayein) ko uske jawab (daayein) se alag karta hai; baayein side padho, jawab dene ki koshish karo, phir daayein se check karo.
Ek concept ek compile-time predicate hai ::: types par (true/false jawab deta hai compile hote waqt, kabhi runtime par nahi).
{ a + b } -> std::same_as<T> test karta hai ::: ke a+b compile hota hai AUR uski type exactly T hai.
Ek unsatisfied constraint cause karta hai ::: candidate ki silent removal, error nahi (error tabhi agar koi na bache).
Jab do constrained overloads dono apply hoten hain aur ek doosre ko subsume karta hai, winner hota hai ::: zyada-constrained wala (subsumption).
Jab do constrained overloads dono apply hote hain lekin koi doosre ko subsume nahi karta, result hota hai ::: ek ambiguity error ("call is ambiguous").
Ek requires-expression bina kisi requirements ke evaluate hota hai ::: true pe (vacuously — empty conjunction).