5.2.31 · D5C++ Programming
Question bank — Inline namespaces, anonymous namespaces
Before the traps, three plain-word anchors so nothing below uses an unearned term:
True or false — justify
An anonymous namespace gives its members internal linkage.
True. Each unnamed namespace acts as a unique hidden name per TU with an implicit
using, so no other TU can name or link to those members — that is the definition of internal linkage.static int x; and namespace { int x; } at file scope are interchangeable for variables.
Effectively yes for a plain variable — both give internal linkage — but the namespace form also works for types, templates and enums, which
static cannot, so prefer the namespace as the uniform tool.An inline namespace makes its members run faster, like an inline function.
False. Same keyword, unrelated mechanism:
inline on a function is an ODR/linkage hint; inline on a namespace is purely about name visibility and versioning — zero runtime effect.inline namespace v2 { ... } inside lib is equivalent to writing using namespace v2; inside lib.
False.
using only imports names for lookup; inline makes lib::T and lib::v2::T the same entity for template specialization and puts v2 into the mangled symbol, which using never does.If two different libraries both use inline namespace v1, their symbols will silently merge.
False — and that's the point. The
v1 is part of each mangled name, so objects built against different v1s stay distinct; the linker fails loudly instead of calling the wrong code.Putting an anonymous namespace in a header keeps its contents private to whoever includes it.
Technically each includer does get a private copy — that's the bug. Every including TU gets its own separate variable/function, so shared-state assumptions break and the binary bloats.
Argument-Dependent Lookup cannot find a function that lives in an inline namespace.
False. Because the inline namespace's members behave as if declared one level up, ADL "sees through" it: the enclosing namespace is treated as an associated namespace of the type.
You must edit every caller's code to migrate them from v1 to v2.
False. If
v2 is the inline (default) namespace, everyone who wrote lib::process() floats forward automatically; only callers who explicitly pinned lib::v1::process() stay on the old one.A namespace can be both named and inline at the same time.
True — that's the normal case (
inline namespace v2). "Inline" and "anonymous" are independent flavors; the versioning idiom deliberately uses a named inline namespace.Spot the error
namespace { struct Cache { int n; }; } placed in cache.h, included by 5 files.
The unnamed namespace belongs in a
.cpp, not a header — each of the 5 TUs now has its own distinct Cache type and any file-local state, causing duplication and subtle "same name, different type" surprises.static struct Node { int v; }; written to make the type file-local.
static cannot give a type internal linkage; it only applies to functions and variables (and here it doesn't even attach to a declared object). Use namespace { struct Node { int v; }; } instead.namespace lib {
inline namespace v1 { void go(); }
inline namespace v2 { void go(); }
}Then a caller writes lib::go();.
Two inline namespaces both leak
go into lib, so lib::go is ambiguous and won't compile. At most one version should be inline (the current default); the rest stay non-inline.// a.cpp and b.cpp both contain, at global scope:
const char* tag = "X";The author expected each file to keep its own tag.
Global-scope non-
const... actually a namespace-scope const char* object here has external linkage, so the linker sees two tag symbols → "multiple definition." Wrap each in an anonymous namespace to make them file-local and distinct.using namespace v2; was used inside lib to "version" the API, then a caller specialized std::hash<lib::T>.
using doesn't make lib::T and lib::v2::T the same entity, so the specialization targets a different name than lookup finds. Only inline namespace unifies them for specialization and mangling.A header declares inline namespace latest { ... } and the .cpp reopens it as namespace latest { ... } (no inline).
Inconsistent: once a namespace is introduced as inline, every reopening should keep it inline. Mixing produces confusing/ill-formed inline-ness and defeats the "one default version" guarantee.
Why questions
Why does an anonymous namespace guarantee no cross-file collision, mechanically?
The compiler invents a unique hidden name per TU, so the fully-qualified name differs in every file; two files can never produce the same mangled symbol.
Why is baking the version into the mangled symbol considered a feature, not a bug?
It turns silent ABI mismatches into loud link errors — code built against
v1 cannot accidentally call v2's differently-behaving function, protecting API evolution.Why does moving one inline keyword suffice to ship a new default version?
The inline namespace is the set of names that leak into the enclosing namespace; relocating
inline from v2 to v3 changes which version unqualified callers resolve to, with no caller edits.Why prefer the anonymous namespace over static in modern C++?
It is uniform — functions, variables, types, templates, and enums all get internal linkage the same way — whereas
static only reaches functions and variables. See static keyword (storage & linkage).Why would putting file-local state in a header quietly corrupt "shared" data?
Because each including TU owns a separate copy, code that assumes a single global counter or cache instead reads/writes many independent copies, so updates in one file are invisible in another.
Why does ADL need to "see through" the inline namespace at all?
So a type declared in
ns::impl can be used with unqualified functions like draw(w) from ns; treating the enclosing namespace as associated keeps the versioning invisible to ordinary ADL usage.Edge cases
What happens if you write lib::process() and no inline namespace exists in lib?
Then
process must be declared directly in lib; with no inline namespace there's nothing to leak upward, so if process only lives in lib::v1, the unqualified lib::process won't be found.What if the same anonymous-namespace name is odr-used in two TUs — is that an ODR violation?
No. They are genuinely different entities (different invented namespaces), so the One Definition Rule is satisfied; there is exactly one definition of each within its own TU.
Can you re-open an anonymous namespace in the same TU to add more members?
Yes — multiple
namespace { ... } blocks in one file all refer to that TU's single unnamed namespace, so members accumulate as expected.What is the linkage of a const-qualified namespace-scope variable, and how does that interact with the anonymous-namespace idiom?
A namespace-scope
const object already has internal linkage by default; wrapping it in an anonymous namespace is harmless and consistent, and is still needed for non-const variables, functions, and types.Is an inline namespace still resolvable by its explicit name after it's made inline?
Yes.
lib::v2::process() still works explicitly; inlining adds the unqualified lib::process() path without removing the qualified one — both name the same entity.What happens at the global (unnamed, top-level) namespace if you nest an anonymous namespace inside another anonymous namespace?
It's legal and simply nests internal linkage inside internal linkage; the members remain TU-local. It's rarely useful but not an error.
Recall One-line summary to lock it in
Anonymous = file-local identity (unique hidden name per TU → internal linkage, never in headers). Inline = default-version identity (names leak up, version baked into the mangled symbol → safe versioning with a one-keyword diff).