5.2.31 · D3 · Coding › C++ Programming › Inline namespaces, anonymous namespaces
Intuition Yeh page kis liye hai
Parent note ne tumhe bataya tha ki do namespace flavors kya karte hain. Yeh page tumhe har woh situation dikhata hai jo real code mein aa sakti hai — "named vs unnamed", "inline vs plain", "ek file vs kai files", "linker complain karta hai?", "kaunsa version call hota hai?" — har combination cover hai. Agar exam mein ya kisi real bug mein koi scenario mile, toh neeche diye cells mein se ek ne woh pehle se dikha rakha hai.
Pehle hum saare cases ka ek full grid banate hain. Phir har worked example ko uss exact cell ke saath tag kiya gaya hai jo woh cover karta hai.
Definition ODR — One Definition Rule
Shuru karne se pehle: ODR ka matlab hai One Definition Rule . Seedhe shabdon mein: ek poore program mein kisi bhi given external entity (yaani koi function ya variable jo files ke across visible ho) ki zyada se zyada ek hi definition ho sakti hai. Ise todo aur linker complain karega — ya phir bura ho sakta hai, silently galat kaam kare. Hum is rule ko poori discussion mein use karte hain, isliye ise apna naam mila. Dekho Translation units and the ODR .
Har namespace question ko ek grid par ek point ki tarah socho. Do sabse important axes hain:
Linkage axis — kya koi naam apni .cpp file se bahar jaata hai? (Yeh internal vs external linkage wala sawaal hai.)
Visibility / versioning axis — kahan se tum naam likh sakte ho, aur kaunsi definition jeetati hai?
Cell
Scenario class
Key question
Example
C1
Do .cpp files mein global naam
Kya linker collide karega?
Ex 1
C2
Do .cpp files mein anonymous namespace
Same naam, koi collision nahi?
Ex 2
C3
Anonymous namespace mein type/template
Kya "sirf functions nahi" wala claim sach hai?
Ex 3
C4
Anonymous namespace header mein (galat use)
Kya toota, aur kitna?
Ex 4
C5
Inline namespace, unqualified call
Kaun sa version default hai?
Ex 5
C6
Inline namespace, explicit v1 call (opt-out)
Kya purane callers purana code pin kar sakte hain?
Ex 5
C7
inline ko naye version par shift karna (limiting case: v3 release karna)
Ek keyword ka migration
Ex 6
C8
[[Argument-Dependent Lookup (ADL)
ADL]] inline namespace ke through
Kya naam unqualified milta hai?
C9
Mangled symbol / ABI (real-world link failure)
Kya version symbol mein bake ho jaata hai?
Ex 8
C10
Exam twist: inline namespace vs inline function
Same word, alag mechanism
Ex 9
C11
Inline namespace header mein kai TUs ke across
Kya identical inline definition ODR todti hai?
Ex 10
Recall Ek sentence mein do axes
Anonymous = internal linkage (linkage axis) ::: yeh decide karta hai ki koi naam apna translation unit chhod ke jaata hai ya nahi.
Inline = visibility + versioning (lookup/ABI axis) ::: yeh decide karta hai ki unqualified default kaunsi definition hai aur symbol kya kehlata hai.
Neeche wali figure is poore grid ko ek picture ki tarah dikhati hai. Abhi padho: neeche ki row mein coral boxes linkage-axis failures hain (Ex 1, aur ABI trap Ex 8); mint / butter boxes "koi problem nahi, yahi fix hai" wale cells hain (Ex 2, Ex 3, Ex 9); vertical axis ke upar lavender boxes visibility / versioning cells hain (Ex 5–7). Beech mein white note woh ek decision rule hai jo yaad karna hai: pehle poochho ki bug kis axis par hai. Neeche har worked example is picture ka ek labelled box hai, toh tum hamesha locate kar sakte ho kahan ho.
Do source files mein se har ek file scope par ek global define karta hai:
// logger.cpp
const char* tag = "LOG" ;
// metrics.cpp
const char* tag = "MET" ;
Dono ko compile karke ek program mein link kiya jaata hai. Linker kya karta hai?
Forecast: padhne se pehle andaza lagao — kya yeh cleanly link hoga, ya fail karega? Agar fail hoga, toh kaisa message aayega?
Steps
Yahan tag ek mutable pointer hai (const char* ka matlab hai "pointer to const char", "const pointer" nahi), toh yeh ek ordinary namespace-scope variable hai jis ki external linkage hai — iska symbol export hota hai.
Yeh step kyun? Koi naam file se bahar jaata hai ya nahi, yeh uski linkage se decide hota hai. Ek plain (non-const-object, non-static) namespace-scope variable ki external linkage hoti hai → iska symbol export hota hai.
Dono files ek tag naam ka symbol export karti hain. Link time par linker ko usi external symbol ki do definitions dikhti hain.
Yeh step kyun? Yahi bilkul One Definition Rule (ODR) hai, jo is page ke upar define ki gayi hai: ek program mein kisi bhi external entity ki zyada se zyada ek hi definition ho sakti hai.
Result: ek link error , multiple definition of 'tag'.
Yeh step kyun? Linker ek nahi chun sakta — woh silently code drop kar deta.
Verify: tag naam ke exported symbols gino: file A ne 1 export kiya, file B ne 1, total 2, allowed maximum 1. Kyunki 2 > 1 , ODR violate hua → link fail. ✅
Ab har global ko ek unnamed namespace mein wrap karo:
// logger.cpp
namespace { const char* tag = "LOG" ; }
// metrics.cpp
namespace { const char* tag = "MET" ; }
Kya ab yeh link hoga?
Forecast: dono files mein same naam — pakka phir clash hogi? Haan/nahi andaza lagao.
Steps
Compiler har unnamed namespace ko aisa rewrite karta hai jaise uska har file ke liye ek unique hidden naam ho, plus ek implicit using:
// logger.cpp ban jaata hai:
namespace __anon_logger { const char* tag = "LOG" ; }
using namespace __anon_logger ;
Yeh step kyun? Woh invented naam har translation unit mein alag hota hai — yahi poora mechanism hai.
Toh do symbols actually __anon_logger::tag aur __anon_metrics::tag hain — distinct names .
Yeh step kyun? Distinct names kabhi collide nahi kar sakte, chahe source mein tumne kuch bhi likha ho.
Har ek ko internal linkage milti hai: apni .cpp ke bahar invisible.
Yeh step kyun? Internal linkage ka matlab hai symbol linker ko cross-file matching ke liye kabhi offer nahi hota.
Verify: tag naam ke exported symbols: file A ne 0 export kiye (internal), file B ne 0 (internal). 0 ≤ 1 → ODR satisfied → link succeeds. Dono tag saath mein exist karte hain. ✅
Ek helper type ko file-local banao:
// cache.cpp
namespace { struct Cache { int n; }; }
int useCache () { Cache c{ 7 }; return c.n; }
Kya tum static struct Cache {...}; likh sakte the? Kya fark hai?
Forecast: andaza lagao ki kya static kisi type ko internal linkage de sakta hai.
Steps
Namespace-scope declaration par static sirf variables aur functions ko internal linkage deta hai, type definitions ko nahi.
Yeh step kyun? static ki grammar simply struct/class/enum/template definitions par internal-linkage rule apply nahi karti. (Dekho static keyword .)
Unnamed namespace, ek namespace hone ki wajah se, andar declare ki gayi har cheez ko internal linkage deta hai — variables, functions, aur types.
Yeh step kyun? Yahi uniformity ki wajah se modern C++ static ki jagah unnamed namespace prefer karta hai.
useCache() c.n return karta hai jahan c.n == 7.
Yeh step kyun? Hum Cache{7} aggregate-initialize karte hain, toh member n mein 7 hota hai.
Verify: Cache{7}.n → single member n 7 set hota hai, toh useCache() 7 return karta hai. ✅
Ek developer yeh config.h mein dalta hai:
// config.h
namespace { int counter = 0 ; }
inline void bump () { ++ counter; }
Files a.cpp aur b.cpp dono #include "config.h" karte hain aur dono bump() call karte hain. a.cpp 3 baar bump() call karne ke baad aur b.cpp 2 baar call karne ke baad, "woh" counter kya hai?
Forecast: andaza lagao ki agar tumne socha yeh ek shared counter tha toh final value kya hogi, vs jo value actually hogi.
Steps
#include header ko textually har .cpp mein paste karta hai. Toh har translation unit ko apna alag unnamed namespace milta hai, isliye apna alag counter.
Yeh step kyun? Internal linkage ka matlab hai "har TU mein ek independent copy" — copies share nahi hoti.
a.cpp ka counter 0 → 3 jaata hai. b.cpp ka counter 0 → 2 jaata hai. Yeh do alag variables hain.
Yeh step kyun? Naive mental model ("counter == 5") bilkul galat hai internal linkage ki wajah se.
Koi single value 5 kaheen nahi hai. Bug silent hai: koi compiler error nahi, bas broken shared-state assumptions aur binary bloat.
Yeh step kyun? Yahi classic rule hai "anonymous namespaces sirf .cpp files mein rakho".
Verify: Do independent counters: TU-a = 3 , TU-b = 2 . Sum 3 + 2 = 5 woh hai jo tum expect karte the; lekin reachable values { 3 , 2 } hain, koi shared 5 nahi. Toh assertion "do counters exist karte hain values 3 aur 2 ke saath, aur koi shared 5 nahi" sach hai. ✅
Versioned math library:
namespace mathx {
namespace v1 { double area ( double r ){ return 3.14 * r * r; } }
inline namespace v2 { double area ( double r ){ return 3.14159 * r * r; } }
}
double a = mathx :: area ( 2.0 ); // (A) unqualified default
double b = mathx :: v1 :: area ( 2.0 ); // (B) explicit legacy
a aur b calculate karo.
Forecast: unqualified mathx::area kaun si value produce karta hai — 3.14 wali ya 3.14159 wali?
Steps
v2 inline declare hai, toh uske naam mathx mein leak ho jaate hain . mathx::area isliye mathx::v2::area resolve karta hai (cell C5).
Yeh step kyun? Inline namespaces enclosing namespace ko unke members bina qualification ke dikhne dete hain — yahi versioning mechanism hai.
a = mathx::v2::area(2.0) = 3.14159 \cdot 2^2 = 3.14159 \cdot 4.
Yeh step kyun? v2 formula mein r = 2 plug karo.
mathx::v1::area fully qualified hai, toh yeh default bypass karke purana code pick karta hai (cell C6).
Yeh step kyun? Explicit qualification hamesha jeetta hai — jo purane callers ne v1 pin kiya tha woh kaam karte rahte hain.
b = 3.14 \cdot 2^2 = 3.14 \cdot 4.
Yeh step kyun? Hum v1 formula ko r = 2 par evaluate karte hain taaki step 2 ke v2 answer se compare kar sakein aur confirm kar sakein ki do versions numerically mein alag hain.
Verify: a = 3.14159 × 4 = 12.56636 , b = 3.14 × 4 = 12.56 . Aur a > b kyunki v2 zyada precise hai (bada π approximation). ✅ (Dekho API versioning .)
Ek saal baad tum v3 ship karte ho. Tum chahte ho ki naye callers automatically v3 paayein jabki v1 aur v2 callable rahe. Ek-keyword change dikhao aur confirm karo ki mathx::area(2.0) ab kya return karta hai.
namespace mathx {
namespace v1 { double area ( double r ){ return 3.14 * r * r; } }
namespace v2 { double area ( double r ){ return 3.14159 * r * r; } } // was inline
inline namespace v3 { double area ( double r ){ return 3.14159265 * r * r; } }
}
Forecast: change ke baad, kya unqualified mathx::area(2.0) ab v2 call karega, ya v3?
Steps
v2 se inline hatao, v3 mein inline add karo.
Yeh step kyun? Sirf ek namespace inline default hona chahiye; default "float" karta hai jahan keyword rakha ho.
mathx::area ab mathx::v3::area resolve karta hai.
Yeh step kyun? v3 ke naam ab mathx mein leak hote hain.
mathx::area(2.0) = 3.14159265 \cdot 4.
Yeh step kyun? v3 formula mein r = 2 plug karo.
mathx::v2::area(2.0) aur mathx::v1::area(2.0) ab bhi compile hote hain aur puri name se apna code call karte hain — kisi ka code nahi toota.
Yeh step kyun? inline hatane se sirf unqualified default change hota hai; purane namespaces ab bhi ordinary named namespaces ki tarah exist karte hain, toh jo bhi caller ne pura path likha tha woh bilkul unaffected hai — yahi migration ko ek safe one-keyword diff banata hai.
Verify: Naya default = 3.14159265 × 4 = 12.5663706 . Yeh strictly purane default 12.56636 se bada hai, confirming karta hai ki default v3 ki taraf aage badh gaya. ✅
namespace ns {
inline namespace impl {
struct Widget {};
void draw ( Widget ){ /* ... */ }
}
}
ns ::Widget w;
draw (w); // unqualified — kya yeh compile hoga?
Forecast: draw bina kisi namespace prefix ke likha hai. Andaza lagao ki compiler ise dhundh payega ya nahi.
Steps
Unqualified draw(w) ADL trigger karta hai: compiler argument ke type Widget ke associated namespaces collect karta hai.
Yeh step kyun? ADL woh rule hai jo tumhe draw jaise free functions ko qualify kiye bina call karne deta hai, type ke home namespace mein search karke.
Widget ns::impl mein rehta hai, lekin kyunki impl inline hai, uske associated namespaces effectively enclosing ns ko bhi include karte hain.
Yeh step kyun? "Inline" ka matlab hai lookup aur ADL inner namespace ke "through" dekhte hain jaise members ns mein hon.
draw us associated set mein mila → call compile hoti hai aur ns::impl::draw call hota hai.
Yeh step kyun? Jab ADL ko sahi namespace mil jaata hai, toh matching overload select hota hai.
Verify: Widget ke associated namespaces = { ns::impl } ∪ { ns } (inline pull-up). draw ∈ us set mein → mila. Toh boolean "draw ADL se resolvable hai" true hai. ✅ (Yeh figure lookup path dikhati hai.)
libgraph.so inline namespace v1 ke saath build hoti hai. App baad mein ek rebuilt library ke against re-link hoti hai jo ab usi function process() ke liye inline namespace v2 use karti hai. Purana object file v1 symbol reference karta hai.
// lib::v1::process() ke liye emit symbol -> _ZN3lib2v17processEv
// lib::v2::process() ke liye emit symbol -> _ZN3lib2v27processEv
Link time par kya hota hai, aur yeh feature kyun hai?
Forecast: andaza lagao ki kya purana object silently naye v2 code se bind hoga, ya link fail karega.
Steps
Inline namespace naam mangled symbol ka part hai: v1 → 2v1, v2 → 2v2 encoded naam mein.
Yeh step kyun? Mangling namespaces encode karta hai taaki alag versions ko alag linker symbols milein.
Purana object ab bhi _ZN3lib2v17processEv maangta hai; naya library sirf _ZN3lib2v27processEv provide karta hai.
Yeh step kyun? Do encoded strings alag hain (2v1 ≠ 2v2), toh woh same symbol nahi hain.
Linker ek unresolved symbol report karta hai silently galat version call karne ki jagah.
Yeh step kyun? Loud failure se better hai silent ABI mismatch jo incompatible code call kare.
Verify: Mangled strings compare karo: _ZN3lib2v17processEv vs _ZN3lib2v27processEv. Woh v1/v2 position par differ karte hain, toh distinct symbols hain → purana reference unresolved hai. Boolean "symbols equal" false hai, yani link correctly fail hoti hai silently versions mix karne ki jagah. ✅
Sach ya jhooth, aur explain karo: "Namespace par inline add karna uske functions ko faster banata hai jaise function par inline karta hai."
Forecast: reasoning se pehle T/F andaza lagao.
Steps
Function par inline ek ODR / linkage relaxation hai: yeh ek definition ko ek aisi header mein appear karne deta hai jo kai TUs include karti hain bina "multiple definition" error ke, aur hint karta hai ki optimizer body inline kar sakta hai.
Yeh step kyun? Keyword ka classic meaning — definitions aur linkage ke baare mein.
Namespace par inline naam visibility aur versioning ke baare mein hai — iska call speed ya code inlining se koi lena dena nahi .
Yeh step kyun? Same spelled keyword, bilkul alag mechanism; inhe confuse karna hi trap hai.
Isliye statement False hai.
Yeh step kyun? Hum true/false prompt explicitly answer karte hain taaki exam-style claim resolve ho.
Verify: Claim ek keyword ke do unrelated meanings confuse karta hai → statement false hai. Boolean = False. ✅
Ex 4 ke anonymous -namespace-in-a-header disaster ke ulta, header mein named/inline namespace dalna ek library ship karne ka normal, correct tarika hai. Consider karo:
// shape.h (a.cpp AUR b.cpp dono include karte hain)
namespace shape {
inline namespace v1 {
inline double area ( double r ){ return 3.14159 * r * r; } // note: inline FUNCTION bhi
}
}
Dono a.cpp aur b.cpp #include "shape.h" karte hain aur shape::area(...) call karte hain. Kya in do TUs ko link karna ODR violate karta hai jaise Ex 1 ne kiya tha?
Forecast: do files, dono mein area ki same definition — Ex 1 jaisi collision, ya theek? Padhne se pehle andaza lagao.
Steps
Inline namespace ki external linkage hai (yeh named hai, anonymous nahi), toh shape::v1::area ko ek shared, exported symbol milta hai — har file ka private copy nahi.
Yeh step kyun? Yeh bilkul Ex 4 ka ulta hai: anonymous → kai internal copies; named/inline → ek external entity. Yahi matrix ke do axes ka fark hai.
Kyunki dono TUs identical token-for-token definition dekhte hain (same header, same inline function body), ODR apna special exception grant karta hai: ek inline entity har TU mein define ho sakti hai, provided sari definitions identical hain.
Yeh step kyun? Isliye library authors kar sakte hain definitions headers mein daalna — function par inline keyword wahi hai jo repeated identical definition ko "multiple definition" error ke bina license karta hai.
Linker identical definitions ko ek shape::v1::area mein merge karta hai aur shape::area har jagah usi par resolve karta hai.
Yeh step kyun? Ek merged symbol ka matlab hai a.cpp aur b.cpp genuinely same code call karte hain — woh shared-state assumption jo Ex 4 ne todi thi woh ab hold karti hai.
Verify: Merge ke baad shape::v1::area ke distinct exported definitions = 1 (identical inline definitions ek mein collapse ho jaate hain). Kyunki 1 ≤ 1 , ODR satisfied hai → link succeed karta hai, aur dono files mein same entity hai — Ex 4 ka mirror image. ✅
Common mistake Ex 4 vs Ex 10 — inhe confuse mat karo
Header mein Anonymous namespace → kai independent internal copies → broken shared state (Ex 4, galat). Header mein inline functions ke saath Named/inline namespace → ODR inline exception ke through ek shared external entity → correct library shipping (Ex 10). "Header" word same hai; linkage opposite hai.
Common mistake Saare cells mein meta-mistake
Do axes ko mix up karna. Agar bug hai "compile nahi hoga / files ke across link nahi hoga," tum linkage axis par ho → anonymous / static / external linkage socho. Agar bug hai "galat version call hua" ya "kaunsa naam default hai," tum visibility axis par ho → inline namespace socho. Pehle poochho kaunsa axis hai; aadha confusion gayab ho jaata hai.
Recall Scenario self-test
Do files mein plain global int x; — link error ya theek? → link error (ODR, dono external)
Same do files mein lekin namespace { int x; } har ek mein — ? → theek, distinct internal symbols
Header mein namespace { int c; }, 2 TUs mein include — kitne c? → do independent copies
Header mein inline namespace v1 { inline double area(...); }, 2 TUs mein include — kitne area? → ek shared entity (identical inline definitions merge ho jaati hain, ODR ok)
inline namespace v2 present hai, caller lib::f() likhta hai — kaun sa version? → v2 (inline default)
inline v2 se v3 par move karo — unqualified callers ab kya paate hain? → v3
Kya v1 vs v2 mangled linker symbol change karta hai? → haan
inline namespace runtime speed affect karta hai? → nahi; sirf visibility/versioning