Intuition What this page is
The parent note told you what a namespace is: a surname box that makes short names unique via the :: operator. This page proves it by hammering the idea against every situation a namespace can land you in — one clean name, two clashing names, nested boxes, file-local boxes, the dangerous using namespace, argument-dependent lookup, inline namespaces, and the exam trick where a leading :: saves you.
One mental model, held fixed for the whole page: a namespace is a labelled box . Its label is glued onto the front of every name inside it, so Geometry::area means "the area that lives in the box labelled Geometry." When you read box below, always picture that: a label + the names it owns. (Elsewhere people say "prefix" — same thing: the label is the prefix. We will only say box .)
Every example is the same question in a new costume: "which full name does the compiler finally pick?"
Before any example, three ideas earn their place, because the table and the examples lean on them: the name-lookup rule , what linkage means, and the One Definition Rule . We build all three now, from zero.
When you write a bare name like f, the compiler gathers every full name that bare f could mean right now into a candidate set , then decides. The figure below draws that rule.
Intuition What the figure shows (in words, in case the image is missing)
A navy box at the top reads "bare name → candidate set" : writing a plain name always produces a set (possibly empty). Three arrows fan down to three outcome boxes:
left, orange — "size 0 → not declared" : the set came out empty.
middle, magenta — "size 1 → compiles" : exactly one full name matched.
right, violet — "size 2+ equal → ambiguous" : two equally-good full names tied.
Under them, a bold navy line says Ns::x or ::x forces the set to size 1 , and a fainter line notes that for function calls overload resolution and ADL may reshape the set before the decision.
Definition The lookup decision, in plain words
Candidate set = every full name (with its box label) that the bare name could refer to given what is currently visible. Lookup always builds this set; it may be empty.
If the set has exactly one entry → that one is chosen, it compiles.
If the set is empty → "not declared in this scope."
If the set has two or more equally-good entries → "ambiguous."
Writing Ns::x (or leading ::x) shrinks the set to one on purpose. That is the whole game.
Two honest complications you will meet on this page, so we name them precisely now:
Overload resolution. If several candidates are functions with the same name but different parameter types, the compiler does not stop at "two candidates → ambiguous." It first tries to pick the best match for your arguments . Only if two are equally good does it call it ambiguous (Example 8).
Argument-dependent lookup (ADL). For a function call f(x), the compiler also silently adds to the candidate set the members named f of the box(es) where the types of the arguments live — even without a using. This applies to every argument (each argument's box is searched) and to operator calls too (a << b searches the boxes of a and b for operator<<). ADL only adds candidates for function/operator calls; it never fires for a plain variable name. We use it in Examples 10 and 11.
Two files (translation units) that will be linked into one program must agree on which names they share . That sharing is governed by linkage .
Linkage answers: "can a name declared in one file refer to the same entity as a name in another file?"
External linkage : the name is shared across files — a global function or variable normally has this. Two files writing that name mean the same thing.
Internal linkage : the name is private to its own file — another file may reuse the spelling for a totally different entity with no conflict.
See Linkage — internal vs external for the full rules; here we only need: external = shared, internal = file-private.
Definition One Definition Rule (ODR)
The One Definition Rule — abbreviated ODR from here on — says: a variable or function with external linkage may be defined exactly once across the entire program. Declare it as many times as you like; define it only once. Two definitions of one externally-linked full name = an ODR violation , caught by the linker. See One Definition Rule (ODR) .
Now we clear every cell of the matrix.
Each row is a case class — a genuinely different situation the topic can throw at you. The examples afterward each fill one or more cells; nothing is left uncovered.
#
Case class
The dangerous input
What decides the answer
C1
One clean name, qualified
none — the easy case
box label forces size-1 set
C2
Two libraries, same short name
A::f and B::f both visible
need qualification to disambiguate
C3
Zero candidates (typo / wrong prefix)
name not in that box
"not declared in this scope"
C4
Shadowing — a local hides a box name
local abs vs std::abs
leading :: / std:: to escape
C5
Nested boxes + alias
very long label A::B::C::x
alias makes it a size-1 shortcut
C6
Open namespace across files (ODR)
same Ns::x defined in 2 .cpps
linker sees 2 definitions → error
C7
Unnamed namespace (file-local)
same helper name in 2 files
internal linkage → no clash
C8
using namespace + overloads
two dumps, then overload resolution
best match wins, else ambiguous
C9
Word problem (real project)
two vendor libs both ship Logger
wrap each vendor in its own box
C10
Exam twist: ::swap + ADL
local using vs std::swap + ADL
leading :: = "start at the top"
C11
ADL corner case
f(obj) finds obj's box for free
argument's box joins the set
C12
Inline namespace
a nested box with no label needed
its names also count as the parent's
Now we clear every cell.
Worked example One clean qualified call
namespace Geometry {
double area ( double r ) { return 3.14159 * r * r; }
}
int main () {
double a = Geometry :: area ( 2.0 ); // = ?
}
Forecast: guess the value of a before reading on. (Circle of radius 2.)
The bare token here is area, but you wrote Geometry::area.
Why this step? The box label Geometry:: forces the candidate set to exactly {Geometry::area} — size 1, no search, no ambiguity possible.
Compute: 3.14159 × 2. 0 2 = 3.14159 × 4 = 12.56636 .
Why this step? With the function chosen, it's just arithmetic — the box did its job before evaluation even starts.
Verify: area of a circle is π r 2 ; with r = 2 , π ⋅ 4 ≈ 12.566 . Units: (length)² ✓. Matches the check 12.56636.
A::f and B::f — pick one
namespace A { int f () { return 1 ; } }
namespace B { int f () { return 2 ; } }
int main () {
// int bad = f(); // would this compile?
int good = A :: f () + B :: f (); // = ?
}
Forecast: Is bare f() legal here? What is good?
Bare f() with nothing made visible: lookup builds the candidate set, and it comes out empty — neither box A nor B is open, and there is no global f. Empty set → "not declared in this scope."
Why this step? A box does not leak its names by default. You must qualify or using them in. (This is the empty-set branch of the lookup rule — the same branch Example 3 studies in depth.)
A::f() → candidate set {A::f} → returns 1. B::f() → {B::f} → returns 2.
Why this step? Different box labels ⇒ different full names ⇒ two coexisting functions, chosen explicitly.
Sum: 1 + 2 = 3 .
Why this step? Once each call has been resolved to exactly one function, the language treats the results as ordinary ints, so we finish with plain addition — the box work is already over and cannot re-introduce ambiguity.
Verify: two distinct full names A::f, B::f. Because their box labels differ, their full names differ, so they can coexist — no collision. Result good = 3 ✓.
Worked example When lookup finds nothing
namespace Statistics { double mean ( double a , double b ){ return (a + b) / 2 ; } }
int main () {
double m1 = Statistics :: mean ( 4 , 10 ); // = ?
// double m2 = Statisics::mean(4,10); // typo in prefix
// double m3 = Statistics::median(4,10);// name not in box
}
Forecast: which of the three lines even compiles? What is m1?
Statistics::mean(4,10) → set {Statistics::mean}, size 1 → compiles. Value ( 4 + 10 ) /2 = 7 .
Why this step? Correct box, correct member.
Statisics::mean (typo Statisics) → the compiler tries to name the box Statisics, finds no such box → "Statisics has not been declared." Lookup still builds a candidate set for the member — but with no box to search inside , the set comes out empty .
Why this step? The lookup rule always builds a set; here the label points at a box that does not exist, so there is nowhere to draw candidates from and the set is empty — the empty-set branch, reported as an undeclared prefix rather than an undeclared member .
Statistics::median → box exists but has no member median → "median is not a member of Statistics." Again an empty candidate set, but now because the (real) box holds no matching name.
Why this step? Right box, wrong member. Zero candidates again, and the message pinpoints the box so you know the prefix was fine and only the member is wrong.
Verify: ( 4 + 10 ) /2 = 7 ✓. Only line 1 survives — the other two are the zero-candidate (empty-set) degenerate cases.
A local name of your own hides a box name. The figure below is the payload of this example — read it before the steps.
Intuition What the figure shows (in words, in case the image is missing)
Three stacked panels are scopes , innermost at the bottom. Lookup for the bare name abs starts in the bottom panel (main) and climbs upward , panel by panel, like an elevator:
bottom (violet) — main() scope: bare abs(-5) (where the search starts);
middle (magenta) — global scope: int abs = 99 ← MATCH here ;
top (orange) — std namespace: std::abs(int) (never reached) .
A solid magenta arrow climbs from the bottom panel to the middle panel and hits a "found → STOP" tag: lookup halts at the first match. A dashed orange arrow to the top panel is drawn faded and labelled "blocked (lookup already stopped)" — std::abs is never even considered . A bold line at the top gives the two escape hatches: std::abs(-5) = 5 or ::abs = 99 .
abs shadows std::abs
#include <cstdlib> // provides std::abs
int abs = 99 ; // your global variable named 'abs'
int main () {
// int x = abs(-5); // (a) what happens?
int y = std :: abs ( - 5 ); // (b) = ?
int z = ::abs; // (c) = ?
}
Forecast: does line (a) compile? What are y and z?
Line (a): bare abs — lookup climbs from main's scope and finds your int abs = 99 first (magenta STOP in the figure). It's an int, not callable, so abs(-5) → "abs is not a function." The name got shadowed .
Why this step? Lookup stops at the first scope yielding a match; it never reaches the std panel above.
Line (b): std::abs(-5) forces the box → std::abs → returns ∣ − 5∣ = 5 .
Why this step? Qualifying jumps straight past your shadowing variable to the library's function.
Line (c): ::abs means "the name abs in the global box" → your variable 99.
Why this step? A leading :: with nothing before it = "start lookup at the very top." Here that top-level abs is your variable, so z = 99. (Contrast: std::abs starts inside the std box.)
Verify: ∣ − 5∣ = 5 so y = 5; z = 99 (your global). Two different abses reached by two different labels ✓.
Worked example A long label, then a shortcut
namespace Company { namespace Project { namespace Net {
int port = 8080 ;
int nextPort () { return port + 1 ; }
}}}
namespace CPN = Company :: Project :: Net ; // alias
int main () {
int a = Company :: Project :: Net ::port; // = ?
int b = CPN :: nextPort (); // = ?
}
Forecast: what are a and b? Do the long name and the alias refer to the same box?
Company::Project::Net::port — chain the labels, outer to inner. The full name is unique, value 8080.
Why this step? Nesting just concatenates box labels; each :: steps one box deeper.
CPN is declared = Company::Project::Net, so CPN:: is a synonym for the whole chain. CPN::nextPort() = Company::Project::Net::nextPort().
Why this step? An alias is a size-1 shortcut — same box, shorter to type, uniqueness preserved.
nextPort() returns port + 1 = 8080 + 1 = 8081.
Why this step? We evaluate the chosen function's body to get the actual answer the caller wanted; resolving the name only told us which function runs — this step runs it, confirming the alias reaches the identical port (8080) that step 1 found.
Verify: a = 8080, b = 8081. Alias and full path name the identical entity (CPN::port == Company::Project::Net::port) ✓.
Ns::x defined in two files
// file1.cpp
namespace Config { int timeout = 30 ; }
// file2.cpp
namespace Config { int timeout = 45 ; } // SAME full name!
Forecast: does this program link ? Which value wins?
A namespace is open : both files reopen the same box Config. So both lines create the full name Config::timeout, which has external linkage (shared across files).
Why this step? Unlike a class body, a box can be reopened anywhere; the label is shared across translation units, so the two timeouts are meant to be the same shared entity.
Now there are two definitions of one externally-linked entity Config::timeout → the ODR (defined earlier) is violated. The linker errors: "multiple definition of Config::timeout." Neither 30 nor 45 "wins" — it never links.
Why this step? ODR says an externally-linked variable may be defined exactly once across the whole program; two definitions break that.
Fix: declare once in a header with extern, define once in a single .cpp:
// config.h
namespace Config { extern int timeout; } // declaration only
// config.cpp
namespace Config { int timeout = 30 ; } // the one definition
Why this step? extern says "this name exists somewhere, don't allocate it here" — a declaration , not a definition, so every file that includes the header learns the name without adding a second definition. Concentrating the single real definition in one .cpp brings the definition count back to exactly 1, satisfying ODR. See Header Files and Include Guards for why the header must be include-guarded so this declaration itself isn't repeated.
Verify (conceptual): count of definitions of Config::timeout = 2 in the broken version (> 1 ⇒ ODR error); = 1 after the fix (ok). The number 2 > 1 is exactly the trigger.
Worked example Same helper name, two files, no clash
// parser.cpp
namespace { int scratch = 0 ; void step (){ scratch ++ ; } }
// writer.cpp
namespace { int scratch = 7 ; void step (){ scratch -- ; } }
Forecast: with the same names scratch and step in both files, does this link? Contrast with Example 6.
An unnamed box gives its members internal linkage — the name is private to its own translation unit (recall: internal = file-private, external = shared).
Why this step? Internally, each file's unnamed box gets a secret unique label, so scratch in parser.cpp and scratch in writer.cpp have different full names invisibly, and neither is shared.
Two definitions of one shared name? No — because the linkage is internal, these are two different entities that merely spell the same, each private to its file. So there is no externally-linked name defined twice → no ODR violation, links fine.
Why this step? ODR only fires on one externally-linked full name defined twice. Internal linkage means the names are not shared, so the C6 trap cannot spring (this is the C2 "different full names ⇒ safe" rule, made automatic by the unnamed box).
This is the modern replacement for writing static at file scope.
Why this step? We flag the idiom because both the old file-scope static and the unnamed box achieve the same effect — internal linkage — but the unnamed box is preferred: it also works for types (a static keyword cannot mark a class), keeping all your file-local helpers in one uniform, greppable place. See Scope and Storage Duration for how linkage and storage duration relate.
Verify (conceptual): count of externally-linked names shared by the two files = 0 here (both scratch/step are internal) ⇒ 0 > 1 is false ⇒ no ODR error. Example 6 had a shared external name defined twice; this one has none.
The figure shows two directives dumping candidates into one pool; the text then shows how overload resolution decides whether that pool is fatal.
Intuition What the figure shows (in words, in case the image is missing)
Two upper boxes — magenta namespace A holding A::f and violet namespace B holding B::f — each send a labelled arrow ("using namespace A" , "using namespace B" ) down into one lower orange pool : "candidate set for bare f() = { A::f , B::f }, size = 2." A bold caption reads: same signature → tie → ambiguous; overloads → best match wins.
Worked example Two dumps — sometimes ambiguous, sometimes not
namespace A { void f () {} int g ( int x ){ return x; } }
namespace B { void f () {} int g ( double x ){ return 0 ; } }
using namespace A ;
using namespace B ;
int main () {
// f(); // (a) compile?
A :: f (); // (b) compile?
int r = g ( 5 ); // (c) which g?
}
Forecast: is bare f() legal? And which g does g(5) call?
Each using namespace (a using-directive ) makes all of that box's names visible at equal rank. Bare f now has candidate set {A::f, B::f} — two functions, identical signatures .
Why this step? They take the same (empty) argument list, so overload resolution cannot prefer one → the "two-or-more equally-good" branch → ambiguous error for line (a).
Line (b) A::f() forces the set to {A::f} → size 1 → compiles.
Why this step? Qualifying always overrides directive-based visibility; it's the escape hatch.
Line (c) g(5) has candidate set {A::g(int), B::g(double)}. Both are visible , so this is not an instant ambiguity — now overload resolution runs: 5 is an int, and A::g(int) is an exact match while B::g(double) needs a conversion. Exact beats conversion → A::g is chosen, returns 5.
Why this step? This is the honest complication we flagged up front: two visible candidates do not automatically mean ambiguity when they are functions — the compiler first tries to pick the best fit for your arguments. Only a tie is fatal.
Verify (conceptual): candidate size for bare f = 2 with equal signatures ⇒ ambiguous. For g(5): exact match A::g(int) outranks the conversion B::g(double) ⇒ unambiguous, returns 5. This is why the parent warns: never using namespace in a header — every includer inherits these risks.
Worked example Two vendors both ship a
Logger
You integrate two libraries. Vendor Acme and vendor Zeta each provide a class Logger with a method warn(int code). Acme's returns code * 2; Zeta's returns code + 100. Both vendors carelessly dumped Logger into the global scope, so including both headers as-is causes a redefinition clash. Your task: use both loggers in one program without editing vendor source .
Forecast: can you keep both Loggers alive at once? What does each warn(3) return?
Wrap each vendor's declarations in a namespace you own — an "adapter box" — at the point you include them:
namespace Acme { class Logger { public: int warn ( int c){ return c * 2 ; } }; }
namespace Zeta { class Logger { public: int warn ( int c){ return c + 100 ; } }; }
Why this step? Distinct box labels give distinct full names Acme::Logger vs Zeta::Logger — the C2 pattern applied to whole classes instead of single functions, so the two Loggers stop being "one name defined twice."
Use them side by side:
Acme ::Logger a; int ra = a. warn ( 3 ); // = ?
Zeta ::Logger z; int rz = z. warn ( 3 ); // = ?
Why this step? Each object's type is fully qualified, so the compiler knows exactly which class's warn to call — no clash, no ambiguity.
Compute: Acme 3 × 2 = 6 ; Zeta 3 + 100 = 103 .
Why this step? With the class chosen, the method body is plain arithmetic; running it gives the two answers the program needs and proves both loggers coexist.
Verify: ra = 6, rz = 103. Both Loggers live because their full names differ ✓. This is exactly the std strategy: the entire standard library lives inside the std box so your names never fight it.
Worked example Force the global one, and watch ADL sneak a candidate in
#include <utility> // std::swap
namespace Tools {
int swap = 42 ; // a variable named 'swap' — nasty!
struct Widget {};
void reset ( Widget & ) {} // lives in the SAME box as Widget
}
int main () {
using namespace Tools ; // now 'swap' the int is visible
int p = 1 , q = 2 ;
// swap(p, q); // (a) compile?
std :: swap (p, q); // (b) after this, p = ? q = ?
int s = :: Tools ::swap; // (c) = ?
Tools ::Widget w;
reset (w); // (d) how is 'reset' found with NO qualifier?
}
Forecast: which lines work? Final p, q, s? And how does bare reset(w) resolve?
After using namespace Tools;, bare swap resolves to Tools::swap, an int (value 42) — not callable . Line (a) swap(p,q) → error, the variable shadowed the function you wanted.
Why this step? The directive injected a matching name for the bare token; lookup finds the int and stops — the C4 shadow, now triggered by a directive. (Note: ADL cannot rescue this — the arguments p, q are plain ints whose "box" is the global scope, which holds no swap function.)
Line (b) std::swap(p,q) forces the std box → the function → exchanges values. Start p = 1 , q = 2 → after swap p = 2 , q = 1 .
Why this step? Qualifying with std:: sidesteps the shadowing variable entirely.
Line (c) ::Tools::swap — leading :: says "start at the global box", then descend into Tools → the int 42. So s = 42.
Why this step? Leading :: guarantees you begin at the very top, immune to any local using.
Line (d) reset(w) has no qualifier and there is no using for reset — yet it compiles and calls Tools::reset. Why? Argument-dependent lookup (ADL) : because the argument w has type Tools::Widget, the compiler automatically adds to the candidate set the reset members of the box where Widget lives, namely Tools. The argument's own box joins the set for free.
Why this step? This is the C11 corner case, and the precise ADL rule from the intro: for a function call , the candidate set gains the matching names from each argument type's box. It's why std::cout << x works with just using std::cout; — operator<< is found via cout's box.
Verify: after std::swap, p = 2, q = 1; s = 42; reset(w) resolves via ADL to Tools::reset. Two names spelled swap (a variable and a function) separated by their boxes ✓.
Worked example A box whose label you can skip
namespace Lib {
inline namespace v2 { // 'inline' = names also count as Lib's own
int api () { return 2 ; }
}
namespace v1 { // ordinary nested box (older version)
int api () { return 1 ; }
}
}
int main () {
int a = Lib :: api (); // (a) = ? which version?
int b = Lib :: v1 :: api (); // (b) = ?
int c = Lib :: v2 :: api (); // (c) = ?
}
Forecast: with no version number, which api does Lib::api() pick?
The inline keyword on box v2 means its members are also treated as members of the enclosing box Lib. So Lib::api and Lib::v2::api name the same function.
Why this step? inline namespace is the standard tool for versioning : you keep old v1 reachable by full name, but make v2 the default that plain Lib::api resolves to — no caller has to type the version.
Line (a) Lib::api() → the inline box's api → returns 2.
Why this step? Because v2 is inline, the label v2 is optional; lookup for Lib::api finds the v2 one.
Line (b) Lib::v1::api() → the explicit older box → returns 1. Line (c) Lib::v2::api() → the same function as (a) → returns 2.
Why this step? Full labels still reach any version explicitly; only the default (unversioned) name changed.
Verify: a = 2, b = 1, c = 2, and a == c (inline box = default). Upgrading the default is a one-word (inline) edit ✓.
Scenario matrix C1 to C12
Every cell C1–C12 is covered, including the degenerate zero-candidate case (Ex3), the file-local no-clash case (Ex7), and the two corner cases reviewers love — ADL (Ex10) and inline namespaces (Ex11).
Recall Quick self-test
A bare name gives an ambiguous error. What single edit always fixes it? ::: Qualify it — write Ns::name (or ::name) to force the candidate set to size 1.
Why does the same helper name in two .cpp files clash if in a named box but not in an unnamed one? ::: Named = same shared (external-linkage) full name defined twice → ODR error; unnamed = internal linkage, each file gets a hidden-unique full name → no clash.
What does a leading :: (nothing before it) mean? ::: Start lookup at the global box, ignoring any local using that shadowed the name.
Two functions with the same name are both visible — is that automatically ambiguous? ::: No. If they are overloads, overload resolution first tries the best match for your arguments; only an exact tie is ambiguous.
How can reset(w) compile with no using and no qualifier? ::: Argument-dependent lookup (ADL) also searches the box of the argument w's type.
Mnemonic The one question behind every example
"Which full name does the compiler finally pick?" — Size 1 ⇒ compiles, size 0 ⇒ "not declared", size ≥ 2 equal-rank ⇒ "ambiguous" (unless overloads let one win). Add a box label to force size 1.
5.2.04 Namespaces — avoiding name collisions (Hinglish)
One Definition Rule (ODR)
Linkage — internal vs external
Scope and Storage Duration
Header Files and Include Guards
Classes and Encapsulation
The Standard Library (std)