Skip to content
VLSI Mentor

C++ Part I · Module 7 · Scope, Storage, and Namespaces

Namespaces: Declaring, Qualifying and Nesting

Chapter 7.5 used a namespace for a peculiar reason — it needed one that could not be named, so that nothing outside a single source file could reach into it. This chapter takes the feature the way it is normally used, which is the exact opposite: a namespace exists so that a name can be reached deliberately and precisely, and so that two independent pieces of code can both use an obvious word without one of them having to give it up. Every large C++ codebase has this problem. Two libraries both want Buffer. Two teams both have a function called read. A vendor header defines something called max and so does the standard library. Without a way to group names, the only remedies are long prefixes on every identifier or an argument about who gets the good name. There is also one property here that is worth meeting carefully, because it makes a namespace unlike every scope in Chapter 7.1: a namespace is not something you define once. The same namespace can be opened again further down the file, or in a different file, by somebody who has never read your code — and that is not a loophole but the mechanism the standard library itself depends on. This is also, finally, where the std:: you have been typing since Chapter 2.8 gets explained.

Foundation16 min readC++Part IFundamentalsScope

Part I · Module 7 · Chapter 7.6 · Namespaces: Declaring, Qualifying and Nesting

1. The Problem, and the std:: You Already Type

Two pieces of code, written by people who have never met, both want the name scale. Or Buffer, or read, or max. Without something to separate them, Chapter 6.8's One-Definition Rule means one of them has to change — and if both are libraries you did not write, neither will.

A namespace is a named region that other names live inside. It turns scale into sim::scale and hw::scale, which are two different names that happen to end the same way.

You have been using one since Chapter 2.8:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    std::cout << "value = " << x << '\n';

std is a namespace — the one the entire standard library lives in. std::cout is the name cout inside the namespace std, and the :: between them is the operator this chapter is about. Every std:: in every example in this curriculum has been a qualified name, and this is the chapter where that stops being punctuation you copy and becomes a thing you understand.

2. Declaring One

Azvya Education Pvt. Ltd.VLSI Mentor
ns.cpp — complete program
#include <iostream>

namespace sim {
    int cycles = 10;
    int scale(int n) { return n * 2; }
}

namespace hw {
    int cycles = 99;
    int scale(int n) { return n * 10; }
}

int main()
{
    std::cout << "sim::cycles = " << sim::cycles << ", hw::cycles = " << hw::cycles << '\n';
    std::cout << "sim::scale(3) = " << sim::scale(3) << ", hw::scale(3) = " << hw::scale(3) << '\n';
    return 0;
}

Measured output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
sim::cycles = 10, hw::cycles = 99
sim::scale(3) = 6, hw::scale(3) = 30

Zero warnings. Two objects called cycles and two functions called scale coexist in one program, and every use says which one it means.

Three mechanical points:

No semicolon after the closing brace. A namespace is not a declaration of a thing; it is a region. namespace sim { … } with no trailing semicolon.

Names inside do not need qualifying from inside. scale could call cycles without writing sim::cycles, because Chapter 7.1's lookup rule searches the enclosing namespace.

Everything in it is at namespace scope. So Chapter 7.3's storage-duration rules apply — sim::cycles has static duration — and Chapter 7.5's linkage rules apply, with external linkage by default.

3. Qualifying a Name

:: is the scope resolution operator. sim::cycles means "the cycles declared in sim".

Chapter 7.1 §4 established that qualification bypasses the ordinary lookup search. That matters most when the nearest declaration is not the one you want:

Azvya Education Pvt. Ltd.VLSI Mentor
nsglobal.cpp — complete program
#include <iostream>

int value = 1;                                     // the global namespace

namespace sim {
    int value = 2;
    int both() { return value * 100 + ::value; }   // sim::value and ::value
}

int main() { std::cout << sim::both() << '\n'; return 0; }

Measured output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
201

Read it: value * 100 found sim::value, which is 2, giving 200. Then ::value — a leading :: with nothing before it — reached the global namespace and found 1. Total 201.

The global namespace is a namespace. It is the outermost one, it has no name, and ::name is how you say "that one, specifically". Chapter 7.1 §5 used exactly this to reach past two shadowing declarations.

4. A Namespace Is Open

Here is the property that makes namespaces different from everything in Chapter 7.1.

A block, a function and a class each exist once. A namespace can be reopened:

Azvya Education Pvt. Ltd.VLSI Mentor
nsopen.cpp — complete program
#include <iostream>
namespace sim { int a = 1; }
namespace sim { int b = 2; }          // reopened, not redefined
int main() { std::cout << sim::a << " " << sim::b << '\n'; return 0; }

Measured output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
1 2

Zero warnings. The second namespace sim { … } did not redefine or shadow the first; it added to it. After both blocks, sim contains a and b.

This is not a curiosity — it is load-bearing, and three things depend on it.

It works across files. Two headers can both contribute to one namespace, which is how any library spread over more than one header presents itself as a single unit.

It is how std works. <iostream> adds std::cout; <algorithm> adds std::count; <string> adds std::string. All of them open the same namespace std and put something in it. There is no single file that declares std.

It means you cannot enumerate a namespace. Given a namespace name, there is no way to ask what is in it, because the answer depends on which headers this translation unit has included. Chapter 7.7's collision problem follows directly from this: using namespace std; brings in whatever std contains here, which is not a fixed set.

A namespace is a region names accumulate in, not a thing that is defined. Nobody owns the complete list.

5. Nesting

Namespaces contain namespaces:

Azvya Education Pvt. Ltd.VLSI Mentor
nsnest.cpp — complete program
#include <iostream>

namespace vendor { namespace driver { int version = 3; } }     // classic form
namespace vendor::codec { int version = 7; }                   // C++17 shorthand

int main()
{
    std::cout << "vendor::driver::version = " << vendor::driver::version << '\n';
    std::cout << "vendor::codec::version  = " << vendor::codec::version << '\n';
    return 0;
}

Measured output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
vendor::driver::version = 3
vendor::codec::version  = 7

Two spellings, same effect. The second is a C++17 addition, and compiling the same file against an earlier standard says so:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ c++ -std=c++14 -Wall -Wextra -Wpedantic nsnest.cpp -o nsnest
nsnest.cpp:4:17: warning: nested namespace definition is a C++17 extension; define each namespace separately [-Wc++17-extensions]

That is the same shape as Chapter 5.1 §6's if-with-initializer: available throughout this curriculum because C++17 is the baseline, and worth knowing is recent because you will read code that predates it.

Nest shallowly. Two levels is normal — a vendor and a component. Four levels means every use site carries a long prefix, and the usual fix is a namespace alias, which gives a short local name for a long one. It is a small feature, Chapter 7.7's neighbourhood, and worth looking up when you need it.

6. One Fact About Lookup From Inside

Chapter 7.1's rule said the search goes outward through enclosing scopes. Namespaces are enclosing scopes, so:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    namespace sim {
        int cycles = 10;
        int scale(int n) { return n * cycles; }    // finds sim::cycles, unqualified
    }

Inside sim, the name cycles is found without qualification. Outside it, cycles alone does not find it at all and sim::cycles is required.

That is all a Module 7 reader needs. There is a larger subject here — how the compiler decides which namespaces to search for a function name based on the types of its arguments — which Chapter 6.7 §5 flagged as one of the things its simplified overload model omitted. It is not taught in this curriculum, and the working rule above covers everything in Part I.

7. What Namespaces Are Not For

Three things worth being clear about, because each is a common misuse.

Not for access control. A namespace does not hide anything. sim::internal_helper is as reachable as sim::scale to anyone who types the qualification. Hiding things from other files is Chapter 7.5's internal linkage; hiding things from other code is Chapter 11.4's access control. A namespace called detail is a convention asking politely, not a mechanism.

Not for organising files. Directory layout and namespace structure often match, and nothing enforces it. A namespace is a naming region; it has no relationship to where source lives.

Not a module. C++20 added a separate feature called modules which addresses the compilation-model problems Chapter 2.5's textual #include creates. Namespaces are about names and nothing else. This curriculum teaches C++17, and the distinction is worth knowing so you do not conflate them when you meet modules elsewhere.

There is also one feature deliberately not covered here — inline namespaces, used for library versioning. You may see one in standard-library source. It is out of scope for Part I.

8. What to Actually Do

Put your own library's names in a namespace. One per library, named after the library.

Qualify at the point of use. sim::scale(n) is three extra characters and says exactly which function it calls.

Keep nesting to two levels. Use a namespace alias when a name is genuinely long.

Use ::name when a nearer declaration is in the way — Chapter 7.1 §5's escape hatch.

Do not expect a namespace to hide anything. Internal linkage (7.5) and access control (11.4) hide; a namespace separates.

Do not try to enumerate one. Its contents depend on what has been included.

Write no semicolon after the closing brace.

Reopen deliberately. Adding to your own namespace in a second header is correct and normal; adding to somebody else's — including std — is not yours to do.

9. Mistakes Worth Making Once

Putting a semicolon after a namespace's closing brace. It is a region, not a declaration.

Expecting a second namespace sim { } to shadow the first. Measured: it added to it, and sim ended up with both names.

Treating a namespace as access control. Anyone can type the qualification; detail is a convention.

Nesting four levels deep. Every call site then carries the whole path.

Expecting cycles to work outside sim. Inside, lookup finds it; outside, the qualification is required.

Forgetting the leading :: when a nearer name is in the way. Measured in Chapter 7.1 §5 and again here — ::value gave 1 where value gave 2.

Using the C++17 namespace a::b form in code that must build as C++14. It is an extension warning there.

Confusing namespaces with C++20 modules. One is about names; the other is about the compilation model.

10. What You Should Be Able to Say Now

  1. A namespace is a named region that other names live in, so two libraries can both use an obvious word.
  2. std is a namespace — every std::cout you have written since Chapter 2.8 was a qualified name.
  3. Measured: two namespaces each holding cycles and scale coexisted — 10 and 99, 6 and 30.
  4. No semicolon after the closing brace.
  5. Inside a namespace, its names are found unqualified; outside, qualification is required.
  6. Everything in a namespace is at namespace scope, so Chapter 7.3's durations and Chapter 7.5's linkage apply.
  7. :: is the scope resolution operator, and a leading :: reaches the global namespace.
  8. Measured: value * 100 + ::value gave 201 — 2 from sim::value, 1 from ::value.
  9. A namespace is open: measured, a second namespace sim { } added to the first rather than redefining it.
  10. That is how std works — every standard header contributes to it, and no file declares it.
  11. So you cannot enumerate a namespace; its contents depend on what has been included.
  12. Nesting works both ways; namespace a::b { } is a C++17 addition that warns as an extension under C++14.
  13. A namespace is not access control, not file organisation, and not a C++20 module.
  14. Openness is why Chapter 7.7 exists — "bring in everything" asks for a set nobody can enumerate.

11. Exercises

Exercise 1 — qualify them. Given namespace sim { int cycles; int scale(int); } and a global int cycles;, write the expression for each: (a) sim's cycles (b) the global cycles (c) sim's scale applied to 3 (d) sim's scale applied to the global cycles.

Exercise 2 — predict the output. For §4's program with a third block namespace sim { int c = 3; } added and sim::c printed, give the output and say what sim contains.

Exercise 3 — spot the errors. Find three mistakes: namespace sim { int a = 1; }; followed by namespace sim { int a = 2; } and a use of a from main.

Exercise 4 — read the 201. Explain each of the two terms in value * 100 + ::value, naming which declaration each found and why.

Exercise 5 — two spellings. Write vendor::codec::version as a nested declaration two ways, and say which requires C++17 and what the other standard reports.

Exercise 6 — the wrong tool. For each goal, say whether a namespace achieves it and name the right mechanism if not: (a) stop another library colliding with your Buffer (b) stop other files calling your helper (c) stop other code touching an object's data (d) group a library's names under one prefix (e) make your headers compile faster.

Exercise 7 — why you cannot ask. Explain why there is no way to list everything in std, and connect that to what using namespace std; actually requests.

12. Summary

  • A namespace groups names so two libraries can both have an obvious one.
  • std is a namespace, and every std:: you have typed was a qualified name.
  • Measured: two namespaces with the same two names — 10/99 and 6/30.
  • No semicolon after the brace; names inside are found unqualified.
  • Namespace scope means Chapter 7.3's durations and Chapter 7.5's linkage apply.
  • :: qualifies, and a leading :: reaches the global namespace — measured, 201.
  • A namespace is open. Measured: a second block added to the first.
  • That is how std is built, and why you cannot enumerate a namespace.
  • Nesting is fine; namespace a::b { } is C++17.
  • Not access control, not file layout, not a module — and openness is why the next chapter exists.

Every name in this chapter was written out in full, which is accurate and gets tiring: a function in a nested namespace, used eight times in one body, is eight long prefixes. C++ has two constructs for shortening that, they differ by one word, and the difference between them is the difference between a convenience and a defect. The next chapter measures both — including a program whose error is reported in a file that does not contain a single using of its own, about a variable its author was perfectly entitled to name, colliding with something from the standard library that the file never asked for.

Standards & specifications

Governing standard
ISO/IEC 14882 (the C++ language standard)(opens ISO/IEC (JTC 1/SC 22/WG 21) in a new tab)

Defines the C++ language and its standard library — the type system and templates, the object model and object lifetime, initialisation and overload resolution, the memory model and the containers, algorithms and utilities the library provides. Like C's standard it is a FAMILY of editions (C++98/03, C++11, C++14, C++17, C++20, C++23) and the editions are NOT interchangeable: a feature this curriculum relies on may be unavailable, differently specified or merely proposed in another edition, so each lesson names the edition its claim depends on. It does not define any foreign-function interface to a hardware description language, nor what a compiler actually emits, nor platform behaviour on real silicon — those are the implementation's and the device's contracts. Working drafts published by WG21 are freely available and are what most engineers read day to day; a draft is not the published standard.

This page also covers RTL structure, verification approach and debugging technique. Those are engineering practice built on the standard, not requirements the standard itself imposes.

Where this fits

Part of the C++ Programming curriculum.