Skip to content
VLSI Mentor

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

using Declarations, using Directives and Name Collisions in Headers

Chapter 7.6 wrote every name 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++ offers two ways to shorten that, and they are separated by a single word. One of them names the thing you want and brings in exactly that; the other names a namespace and brings in whatever that namespace happens to contain at this point in this translation unit — a set which, as the previous chapter established, nobody can enumerate, because it depends on which headers have been included. That difference is why one construct is a convenience and the other is a defect in the one place it does the most damage. This chapter is not going to assert that using namespace std is bad style. It is going to compile a program that contains no using of any kind, declares a variable with a perfectly ordinary name its author was entitled to choose, and fails — with an error reported in that file, about that line, caused by one line in a header the author may never have opened. Then it removes that line and compiles the same program cleanly. The last section is about where each construct genuinely belongs, because a rule with no boundaries gets ignored.

Intermediate17 min readC++Part IFundamentalsScope

Part I · Module 7 · Chapter 7.7 · using Declarations, using Directives and Name Collisions in Headers

1. Two Constructs, One Word Apart

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    using sim::cycles;        // a using DECLARATION  -- one name
    using namespace sim;      // a using DIRECTIVE    -- every name

The difference is the word namespace, and it is the difference between naming what you want and naming where to look.

Both modify Chapter 7.1's lookup rule. A using declaration adds one name to the current scope. A using directive makes every name in a namespace a candidate for lookup in the current scope.

Chapter 7.6 §4 established the fact that makes the second one dangerous: a namespace is open, so nobody can enumerate its contents. using namespace std; does not request a known list. It requests whatever std contains in this translation unit, which depends on which headers have been included — here, and in every header those headers include.

2. A using Declaration

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

namespace sim {
    int cycles = 10;
    int depth  = 4;
}

int main()
{
    using sim::cycles;            // a using DECLARATION: one name
    std::cout << "cycles = " << cycles << '\n';
    std::cout << "depth  = " << sim::depth << " (still needs qualifying)\n";
    {
        using namespace sim;      // a using DIRECTIVE: every name
        std::cout << "inside the directive: depth = " << depth << '\n';
    }
    return 0;
}

Measured output:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
cycles = 10
depth  = 4 (still needs qualifying)
inside the directive: depth = 4

Zero warnings. Three readings:

using sim::cycles; brought in exactly one name. After it, cycles means sim::cycles.

depth was unaffected. It still needed sim::depth, because the declaration named cycles and nothing else. That selectivity is the whole value.

Inside the inner block, the directive made depth work unqualified — and only inside that block. A using construct obeys Chapter 7.1's scope rules like any other declaration: it lasts until the end of the enclosing scope.

A using declaration is a declaration, which means it collides like one. Two of them bringing in the same name from different namespaces is an error, measured:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
twodecl.cpp:6:14: error: target of using declaration conflicts with declaration already in scope
twodecl.cpp:2:19: note: target of using declaration
twodecl.cpp:1:19: note: conflicting declaration

Reported at line 6 — the second using declaration, where you wrote it — and both conflicting namespaces are named. That is the opposite of §6's behaviour, and it is the reason a declaration is the safer construct: it fails at the point of the decision rather than at the point of a later use.

3. A using Directive

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    using namespace sim;

This does not bring names into the scope. It makes them visible to lookup in it, which is a subtler thing with one practical consequence: a directive loses to a local declaration rather than conflicting with it.

Azvya Education Pvt. Ltd.VLSI Mentor
loses.cpp — complete program
#include <iostream>
namespace sim { int cycles = 10; }
int main()
{
    using namespace sim;
    int cycles = 99;              // does this conflict, or win?
    std::cout << "cycles = " << cycles << "   sim::cycles = " << sim::cycles << '\n';
    return 0;
}

Measured output, with zero diagnostics:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
cycles = 99   sim::cycles = 10

Chapter 7.1's rule explains it: the local declaration is in a nearer scope, so the search stops there. No ambiguity, no diagnostic, and sim::cycles is still there — reachable only by its qualified name.

That is the directive's real character. It does not announce conflicts; it quietly loses them, and the only ones it reports are the ones it cannot resolve. §5 is what the second kind looks like.

4. Why the Two Are Not Interchangeable

using declarationusing directive
Brings inone name you namedevery name the namespace has
The set isfixed by youwhatever has been included
A conflict isreported at the usingreported at the use, or silently lost
Readable?yes — the name is listedno — nothing says what came in
Safe in a header?with careno — §6

The decisive row is the second. A using declaration's effect is written down: using sim::cycles; says what arrived. A directive's effect is not written anywhere, cannot be, and changes over time — add an #include to a header three directories away and the set grows.

5. The Collision, in One File

Azvya Education Pvt. Ltd.VLSI Mentor
collide.cpp — complete program. Does not compile.
#include <algorithm>
#include <iostream>

using namespace std;          // the defect

int count = 0;                // a perfectly reasonable name

int main()
{
    cout << "count = " << count << '\n';
    return 0;
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
collide.cpp:10:27: error: reference to 'count' is ambiguous
collide.cpp:6:5: note: candidate found by name lookup is 'count'
/…/c++/v1/__algorithm/count.h:86:1: note: candidate found by name lookup is 'std::count'

std::count is a standard algorithm that lives in <algorithm>. The directive made it a candidate for the unqualified name count, and the file's own count is a candidate too. Neither is nearer — both are at namespace scope as far as lookup is concerned — so there is no "first hit" and the search cannot resolve.

The same program without the directive:

Azvya Education Pvt. Ltd.VLSI Mentor
nocollide.cpp — compiles and runs
#include <algorithm>
#include <iostream>

int count = 0;                // no directive, so no ambiguity

int main()
{
    std::cout << "count = " << count << '\n';
    return 0;
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
count = 0

Zero warnings. Identical count, identical includes. The only change is the removed directive.

Note what kind of error this is. Chapter 6.7 §4 measured an ambiguous call — two candidate functions, neither better. This is an ambiguous name: lookup found two declarations and stopped before overload resolution was ever reached. They read similarly and are different stages.

6. The Same Collision, From a Header

Now the measurement this chapter exists for.

Azvya Education Pvt. Ltd.VLSI Mentor
sim.h
#ifndef SIM_H
#define SIM_H
#include <algorithm>          // the header needs it for its own reasons
using namespace std;          // THE DEFECT: every includer inherits this
namespace sim { int cycles(); }
#endif
Azvya Education Pvt. Ltd.VLSI Mentor
user.cpp — contains no using of any kind
#include "sim.h"              // user.cpp never asked for `using namespace std`
#include <iostream>

int count = 0;                // a perfectly reasonable name in MY file

int main()
{
    std::cout << sim::cycles() << " " << count << '\n';
    return 0;
}
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ c++ -std=c++17 -Wall -Wextra -Wpedantic -c user.cpp -o user.o
user.cpp:8:42: error: reference to 'count' is ambiguous
user.cpp:4:5: note: candidate found by name lookup is 'count'
/…/c++/v1/__algorithm/count.h:86:1: note: candidate found by name lookup is 'std::count'

Read where everything is.

The error is in user.cpp. Line 8.

It is about user.cpp's own declaration. Line 4 — int count = 0;, a name its author chose and was entitled to choose.

The cause is in neither place. It is one line in sim.h, which user.cpp's author may never have opened, and which they cannot change if sim.h belongs to somebody else.

user.cpp contains no using at all. Not a declaration, not a directive.

That is the whole answer to "exactly why using namespace std at header scope is a defect", and it is a mechanical answer rather than a stylistic one. Chapter 2.5 established that #include is textual: the header's lines become the including file's lines. So a directive in a header is a directive in every file that includes it, transitively, forever — and the damage is reported in the victim's file, about the victim's code.

Three properties make it worse than an ordinary mistake:

It scales with your users. One line affects every file that includes the header, including files written later by people you will never meet.

It is undiagnosable from the error. Nothing in that message mentions sim.h or the directive. A reader's first instinct is that their own count is the problem.

It gets worse without anyone touching it. Add #include <algorithm> to a header that sim.h includes, and files that compiled yesterday stop compiling — with the error in their file.

7. The Fix

Remove the directive from the header. Nothing else changes:

Azvya Education Pvt. Ltd.VLSI Mentor
sim2.h
#ifndef SIM2_H
#define SIM2_H
#include <algorithm>
namespace sim { int cycles(); }      // no directive; the header stays clean
#endif
Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
$ c++ -std=c++17 -Wall -Wextra -Wpedantic user2.cpp sim2.cpp -o prog2
$                                            ← zero diagnostics
$ ./prog2
10 0

The identical program compiles and runs. If the header's own implementation wants shorter names, the directive belongs in sim2.cpp, where it affects one translation unit that chose it.

A header is read by people who did not write it and cannot change it. Anything a header does to the lookup rule, it does to all of them.

8. Where Each Construct Belongs

A prohibition with no boundaries gets ignored, so here is the full position.

A using directive inside a function: fine. §2's inner block. Its effect ends at the closing brace, it is visible to anyone reading the function, and the names it could collide with are the ones in front of you.

A using directive in a source file, after the includes: defensible. It affects exactly one translation unit, whose author chose it. It is still the construct that loses conflicts silently, so it is a trade rather than a free choice — and using namespace std; specifically is a poor trade even here, because std is enormous and full of ordinary words: count, distance, size, data, swap, max, end.

A using directive in a header: never. §6 is the reason.

A using declaration in a function: good. using sim::cycles; at the top of a body that uses it eight times is clearer than eight qualifications and says exactly what arrived.

A using declaration in a header: with care. It brings in one named thing, so the blast radius is one name rather than a namespace — but it is still imposed on every includer. Reserve it for cases where the header genuinely needs the name in its own declarations.

A namespace alias is the underused answer to the problem that actually motivates most directives:

Azvya Education Pvt. Ltd.VLSI Mentor
Snippet
    namespace vc = vendor::codec;      // a short local name for a long one
    vc::version

It shortens without bringing anything in. Chapter 7.6 §5 mentioned it; this is where it earns its place.

And std:: is three characters. Typing it is the normal thing in professional C++, it marks standard-library names as standard-library names at every use site, and it never collides.

9. Three Things Called using

The keyword is reused, and knowing there are three meanings stops a later one looking like a contradiction.

FormMeansWhere
using sim::cycles;a using declaration — one namethis chapter
using namespace sim;a using directive — a namespacethis chapter
using Count = int;a type alias — a new name for a typeModules 3 and 9

A fourth appears in Module 16, where using Base::f; brings a base class's member into a derived class. It is the same keyword doing a related job in a different place.

Only the first two are this chapter's. The type-alias form has nothing to do with namespaces and does not affect lookup.

10. What to Actually Do

Qualify by default. std:: is three characters and never collides.

Use a using declaration in a function when one name appears many times in one body.

Use a namespace alias when the problem is a long namespace rather than a repeated name.

Never put a using directive in a header. §6 is what it does.

Keep a using directive inside a function if you want one at all.

Avoid using namespace std; even in a source file. std is large and full of ordinary words.

Remember a directive loses silently. A local declaration shadows it with no diagnostic.

When an ambiguity error names your own variable, check what your headers brought in. The error will not tell you.

11. Mistakes Worth Making Once

A using directive in a header. Measured: the error lands in a file with no using, about its own variable.

Assuming the error points at the cause. Measured: it names user.cpp:8 and user.cpp:4, and never mentions the header.

Naming a variable count, size, data or end under a std directive. All are ordinary words and all are in std.

Expecting a directive to report a conflict with a local. It loses silently — the local wins and nothing is said.

Confusing an ambiguous name with an ambiguous call. Lookup found two declarations; Chapter 6.7 §4's case is two viable functions.

Thinking a using declaration in a header is as bad as a directive. It is one name rather than a namespace — still imposed, far smaller.

Reaching for a directive when the real problem is a long namespace. A namespace alias shortens without importing.

Reading using X = int; as a namespace feature. It is a type alias — §9.

12. Interview Relevance

"What is the difference between a using declaration and a using directive?" One brings in a single named entity; the other makes every name in a namespace visible to lookup. The sharper half of the answer is that the directive's set is not enumerable — it depends on what has been included — which is the reason the two are not interchangeable.

"Why is using namespace std; bad?" The weak answer is "it pollutes the global namespace". The strong answer distinguishes placement: in a function it is fine, in a .cpp it is a defensible trade, and in a header it is a defect — because #include is textual, so it is imposed on every including file, and the resulting error is reported in the victim's file about the victim's own declaration. If you can name a colliding pair — your count against std::count — you are describing something you have hit.

"How would you shorten a long name without that?" A using declaration for one name, or a namespace alias for a long namespace. Candidates who know the alias usually know it because they needed it.

13. What You Should Be Able to Say Now

  1. A using declaration brings in one name; a using directive makes every name in a namespace visible to lookup. The difference is the word namespace.
  2. Both modify Chapter 7.1's lookup rule, and both obey its scope rules.
  3. The directive is dangerous because Chapter 7.6 §4's openness means its set cannot be enumerated and changes with your includes.
  4. Measured: using sim::cycles; brought in cycles only — depth still needed sim::depth.
  5. Measured: a directive in an inner block made depth work unqualified, and only there.
  6. A directive loses to a local declaration silently — the nearer declaration wins, with no diagnostic.
  7. Measured: using namespace std; plus int count = 0; gives error: reference to 'count' is ambiguous, with both candidates named.
  8. Measured: removing the directive makes the identical program compile and print count = 0.
  9. That is an ambiguous name, not Chapter 6.7 §4's ambiguous call — lookup stopped before overload resolution.
  10. Measured, and the chapter's point: a directive in a header made user.cpp fail, in user.cpp, about user.cpp's line 4, while user.cpp contained no using at all.
  11. The cause is in neither place the error names, and the message never mentions the header.
  12. It scales with your users, is undiagnosable from the error, and gets worse when somebody adds an #include elsewhere.
  13. Placement is the issue, not the construct: fine in a function, defensible in a .cpp, never in a header.
  14. A namespace alias shortens a long namespace without importing anything, and using has three meanings — declaration, directive, and type alias.

14. Exercises

Exercise 1 — which construct? For each, say whether it is a using declaration, a using directive, or a type alias: (a) using std::cout; (b) using namespace std; (c) using Width = int; (d) using sim::scale; (e) namespace vc = vendor::codec;.

Exercise 2 — predict the output. For §2's program with the inner block's directive removed, say which line fails and what the compiler reports.

Exercise 3 — trace the ambiguity. For §5's failing program, say which two declarations lookup found, why neither is nearer, and at which stage the compilation stopped.

Exercise 4 — locate the cause. Given §6's error output alone, list what the message tells you and what it does not. Then give the two commands or checks you would use to find the actual cause.

Exercise 5 — fix it four ways. sim.h has using namespace std; and users are colliding. Give four different repairs and say which is correct and which merely postpone the problem.

Exercise 6 — judge the placement. For each, say fine, defensible, or defect, with a one-clause reason: (a) using namespace std; inside one function (b) the same at the top of a .cpp (c) the same in a header (d) using std::cout; in a header (e) namespace vc = vendor::codec; in a header.

Exercise 7 — the general rule. §8's callout claims the reasoning extends beyond using. Give two other things a header can do that impose on every includer, and state the single principle all three violate.

15. Summary

  • A declaration brings in one name; a directive makes a whole namespace visible to lookup.
  • The directive's set is not enumerable — Chapter 7.6 §4 — and grows with your includes.
  • Measured: a declaration brought in cycles only; a directive in a block worked only in that block.
  • A directive loses to a local silently.
  • Measured: using namespace std; with an int count gives reference to 'count' is ambiguous; removing it compiles.
  • That is an ambiguous name, before overload resolution.
  • Measured, the chapter's point: a directive in a header broke user.cpp in its own file, about its own line, with no using anywhere in it.
  • The error never names the cause, the damage scales with your users, and it worsens from an unrelated #include.
  • Placement is the defect: fine in a function, defensible in a .cpp, never in a header.
  • Namespace aliases shorten without importing; using has three meanings.

Module 7 is finished, and its three questions are all answered. Scope is where a name may be written, settled at compile time by one lookup rule. Lifetime is when an object exists, determined by which of four storage durations it has rather than by where its name appears — and Chapter 7.2 measured both ways in which the two come apart. Linkage is whether another translation unit can reach a name, visible in a single character of a symbol table. Separating them is what turned static from a keyword with two confusing uses into two unrelated features that share a spelling, and what turned a dangling reference from a rule to memorise into a corollary you can derive.

Module 8 is where that last point pays. Its subject is pointers and references, and its blurb makes an unusual claim: nothing in it is new machinery. Module 3 established that an object is typed storage at an address. Module 6 established that a call copies its arguments. Module 7 has now established that an object's lifetime is independent of any name for it. A pointer is the type that holds an address; a reference is another name for an object — and Chapter 6.4 already measured one of those doing exactly that. What Module 8 adds is not a mechanism but a consequence: once a program can hold the address of an object, it becomes possible to refer to storage whose object has ceased to exist, and Chapter 8.8 is the chapter that enumerates every way that happens. Chapter 7.2 built the model it needs.

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.