5.2.18 · D2 · HinglishC++ Programming

Visual walkthroughConcepts (C++20) — constraining templates

2,526 words11 min read↑ Read in English

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.

Figure — Concepts (C++20) — constraining templates

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).

Figure — Concepts (C++20) — constraining templates

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.

Figure — Concepts (C++20) — constraining templates

Step 4 — Bouncer ko lagaana: chaar darwaaze, ek hi guard

KYA HAI. Neeche charon ek hi guarded function sq stamp karte hain:

  1. Abbreviated parameter — guard parameter par hi sawaar hai:
  2. Constrained template parameter — guard type list mein typename ki jagah leta hai:
  3. Leading requires-clause — guard template head ke baad ek alag clause hai:
  4. 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.

Figure — Concepts (C++20) — constraining templates

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 b kaalfarz hain — kabhi actually construct nahi hote. Woh sirf isliye hain taake hum a+b likh 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 exactly T pin 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.

Figure — Concepts (C++20) — constraining templates

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> = falseremove, 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.

Figure — Concepts (C++20) — constraining templates

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.

Figure — Concepts (C++20) — constraining templates

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.

Figure — Concepts (C++20) — constraining templates
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).