Home
>
Articles
>
Programming
>
C/C++
This chapter is from the book
3.6 Namespaces
An earlier section ("Storage Classes" on page 124) explains scope
rules for variables in C++. Program variables may be:
Local (declared inside functions without static and exist in
local stack frames)
External static with file scope (declared outside functions with
static)
Global with program scope (declared outside functions without
static)
Internal static (declared inside functions with static and exist
in the data area)
With program scope (3), separately compiled modules require access to
function names, structures, and objects, often placed in global namespace.
Global namespace includes all names from global declarations; that is,
declarations appearing outside of function definitions. What is the proper way
to deal with global namespaces? This problem can be significant, especially with
applications and libraries. Let's investigate how namespaces can help.
Why Use Namespaces?
When programmers use separately compiled modules, name conflicts may occur in
the global namespace. Consider the following geometry.h include file, for
instance.
// geometry.h
struct Point { double x, y; }; // global structure name
double slope(Point, Point); // global function prototype
The compiler enters the names for the Point structure and slope()
function into the global namespace. Suppose a separate geometry.C module
contains the slope() implementation and defines origin, a
global variable.
// geometry.C
#include "geometry.h"
Point origin = { 0, 0 }; // global variable name
double slope(Point a1, Point a2) // function implementation
{ . . . }
Another module mymath.C includes geometry.h. This file contains functions
that call slope(), but it also defines its own variable origin
as global.
// mymath.C
#include "geometry.h"
Point origin = { 10, 10 }; // link error, name defined twice
. . .
There are no compilation errors with these modules. At link time, however,
errors occur because the global name origin is defined twice. Note that
type Point is not the problem here; rather, the global name (origin)
can only be seen once by the linker.
We can easily fix the problem by renaming origin in mymath.C.
Point myorigin = { 10, 10 }; // global variable name
However, this solution is not always possible or desirable. Renaming
origin could affect more than one function defined in mymath.C. With
namespaces, we have more choices. Instead of only two options for names (global
and nonglobal), we divide a global namespace into separate "named"
scopes. This approach lets us access variables from these namespaces with a
special syntax.
The following sections show you how to define namespaces, access members of
namespaces, and bring namespace variables into local scope. Namespaces also let
you create shorthand names (aliases) for longer namespace qualifiers. Namespaces
are particularly helpful with resolving name conflicts when you create class
libraries. We examine this use of namespaces in "Namespaces and
Classes" on page 254.
Namespace Definitions
The keyword namespace defines a namespace.
namespace name {
namespace_body
}
This format creates a namespace with qualifier name. Within the
braces, namespace_body may include variables, function definitions and
prototypes, structures, classes (see "Classes" on page 177), enums,
typedefs, or other namespace definitions (see "Nested Namespaces" on
page 139). Note that a namespace definition does not terminate with a
semicolon.
Namespace definitions appear in header files and in separate modules with
function definitions. Here's an example.
namespace Red { // define namespace Red
int j; // j is a member of namespace Red
void print(int i) // print() is a member of namespace Red
{ cout << "Red::print() = " << i << endl; }
}
namespace Blue { // define namespace Blue
int j; // j is a member of namespace Blue
void print(int); // print() is a member of namespace Blue
}
void sub1() { . . . } // may access Blue and Red namespaces
void sub2() { . . . } // may access Blue and Red namespaces
Namespace Red has two members: integer j and function
print(). Namespace Blue has two members with the same names. Normally,
these definitions clash in global namespace, but separate namespace qualifiers
(Red and Blue) eliminate the problem. Note that a namespace may include function
definitions and function prototypes. The function definitions for
sub1() and sub2() have access to all of the Red and Blue
namespace members, as you will see shortly.
NOTE
Namespace definitions cannot appear inside functions; that is, they must
appear outside functions at global scope.
int sub3() {
namespace Green { // error, defined inside function
char ch;
char buffer[1024];
}
. . .
}
Namespace Extensions
Namespaces are extensible; that is, you can append subsequent declarations to
previously defined namespaces. Namespace extensions may also appear in files
separate from the original namespace definition. The following two definitions
of namespace Blue are equivalent to the definition of namespace BigBlue.
namespace Blue { // original namespace definition
int j;
void print(int);
}
namespace Blue { // namespace extension
char ch;
char buffer[20];
}
. . .
namespace BigBlue { // equivalent to the above
int j;
void print(int);
char ch;
char buffer[20];
}
When you create a namespace, avoid placing include files inside the namespace
definition.
// myfile.C
namespace math {
#include "geometry.h" // not a good idea
Point origin = { 3, 5 };
. . .
}
This approach creates link errors with other modules that do not include
geometry.h in the same namespace. Instead, define a namespace in the include
file itself, and use namespace extensions for modules that need to add to
it.
// geometry.h
namespace math { // define namespace
struct Point { double x, y; };
double slope(Point, Point);
}
// myfile.C
#include "geometry.h"
namespace math { // namespace extension
Point origin = { 3, 5 };
. . .
}
Accessing Namespace Members
The scope resolution operator (page 67) provides access to namespace
members.
namespace_name::member_name
namespace_name is a previously defined namespace qualifier, and
member_name must be declared inside namespace_name.
Here's an example with our Red and Blue namespaces.
namespace Red { // define namespace Red
int j; // j is a member of namespace Red
void print(int i) // print() is a member of namespace Red
{ cout << "Red::print() = " << i << endl; }
}
namespace Blue { // define namespace Blue
int j; // j is a member of namespace Blue
void print(int); // print() is a member of namespace Blue
}
void Blue::print(int k) {
cout << "Blue::print() = " << k << endl;
}
Outside the Blue namespace definition, the scope resolution operator
associates print() with namespace Blue. Without it, the compiler
defines print() as a global function. Here are several more examples
with variables and function calls.
void sub() {
Red::j = 0; // member of namespace Red
Red::print(5); // member of namespace Red
j = 5; // illegal, no local j defined
int j = 10; // legal, j is a local variable
Blue::j = 5; // member of namespace Blue
}
Inside sub(), the scope operator qualifies the first assignment to
j and the call to print() from namespace Red. The last
assignment uses j from namespace Blue. Without the scope operator and a
namespace qualifier, the compiler reports an error in the second assignment.
Note that we may declare and initialize a local j inside sub()
without any conflict from namespace Red or Blue.
Unnamed Namespaces
A variable or function defined outside function definitions with the
specifier static creates a name in file scope; that is, an external
static known only within the file where it's declared (see
"static" on page 125). External statics do not conflict with the same
names defined in different translation units at link time. (A translation unit
is a source code file with include files after preprocessing.) Unnamed
namespaces make external statics obsolete in ANSI C++. Here's the
format.
namespace {
namespace_body
}
A left brace immediately follows the keyword namespace without an
intervening name qualifier. All members defined in namespace_body are
in an unnamed namespace that is guaranteed to be unique for each translation
unit. At link time, member names in one unnamed namespace do not conflict with
equivalent member names from other unnamed namespaces in separate translation
units.
The following example shows why unnamed namespaces are useful.
namespace { // unnamed namespace
const int Max = 20;
int buffer[Max];
int counter;
}
void sub1() {
counter = 0; // counter in unnamed namespace
for (int i = 0; i < Max; i++) // Max in unnamed namespace
buffer[i] = i*2; // buffer in unnamed namespace
. . .
}
Unnamed namespaces permit access to Max, buffer, and
counter without the scope resolution operator. An unnamed namespace
also solves our linkage problem in mymath.C (page 134).
// mymath.C
#include "geometry.h"
namespace {
Point origin = { 10, 10 }; // no conflict with geometry.h
}
. . .
Unnamed namespace extensions are possible, but only within the same
translation unit.
// file.C
namespace { // unnamed namespace
int j;
void print(int); // prototype
}
namespace { // same unnamed namespace
void print(int i) { cout << i << endl; }
}
void sub() {
j = 3;
print(j); // no ambiguity
}
Inside file.C, the prototype and definition for print() are members
of the same unnamed namespace. The second unnamed namespace extension
surrounding the print() definition is important because it tells the
compiler that print() is not global. Without it, calls to
print() are ambiguous because the compiler cannot distinguish between a
global function name and a member of an unnamed namespace.
NOTE
With unnamed namespaces, make sure your function implementations are either
inside the namespace definition directly or a member of an unnamed namespace
extension in the same translation unit.
Here's another example that shows you how global variable scope differs
from unnamed namespace scope.
namespace { // unnamed namespace
const int Max = 20;
char buffer[Max];
int counter;
}
int counter = 1; // legal - global variable
void sub2() {
counter++; // illegal - ambiguous (global or unnamed)?
::counter = 5; // legal - global
int counter = 0; // legal - hides global and unnamed member
counter = 10; // legal - local variable
. . .
}
Inside sub2(), counter++ is ambiguous because the compiler
cannot distinguish between a global name and an unnamed member with the same
name. Furthermore, the scope resolution operator without a namespace qualifier
(::counter) refers to a global name, not an unnamed member. It's
also possible to declare a local counter name, but be careful with this
technique. Here, the local counter hides both the global
counter and the unnamed member.
NOTE
Unnamed namespaces restrict global variables to file scope. Use unnamed
namespaces in place of external static. Don't use :: to refer to
unnamed members, and don't mix globals with unnamed members if they have
the same name.
Nested Namespaces
Namespace definitions are illegal inside functions, but they may appear
inside other namespace definitions. This approach nests namespaces for better
data hiding. Here's an example.
namespace Outer { // namespace definition
int i, j;
namespace Inner { // nested namespace
const int Max = 20;
char ch;
char buffer[Max];
void print();
}
}
For proper access to members of a nested namespace, the scope resolutions
operator (::) becomes very important.
void Outer::Inner::print() {
for (int i = 0; i < Max; i++) // i is local to for loop
cout << buffer[i] << ' ';
cout << endl;
}
Two levels of scope resolution are necessary to define the print()
function of Inner. Note that we can access ch, buffer, and
Max without the scope operator inside print().
The following sub() function shows more examples of accessing
members of Outer and its nested Inner namespace.
void sub() {
Outer::i = 10;
Outer::Inner::ch = 'a';
for (int i = 0; i < Outer::Inner::Max; i++) // i is local
Outer::Inner::buffer[i] = Outer::Inner::ch + i;
Outer::Inner::print();
cout << "Max = " << Outer::Inner::Max << endl;
}
Using Directives
Accessing namespace members can be cumbersome, especially with long namespace
qualifiers and nested namespaces. Fortunately, there are several ways to bypass
namespace qualifiers and make access to namespace members more convenient. Using
directives provide access to all namespace members without a namespace qualifier
and the scope operator. Here is the format.
using namespace name;
The qualifier name following the keywords using and
namespace must be a previously defined namespace. Using directives may
appear at global scope and local scope (inside blocks, functions, and namespace
definitions).
Global Using Directives
Let's return to our Blue namespace to show you a using directive at
global scope.
namespace Blue { // define namespace Blue
int j; // j is a member of namespace Blue
void print(int); // print() is a member of namespace Blue
}
using namespace Blue; // global using directive
void sub1() {
j = 0; // legal - Blue::j
print(j); // legal - Blue::print()
. . .
}
void sub2() {
j = 1; // legal - Blue::j
print(j); // legal - Blue::print()
. . .
}
The global using directive provides access to j and print()
from namespace Blue without a qualifier and without the scope operator. Note
that j in sub1() and sub2() are not local
variables.
NOTE
Be careful with local variables in functions that hide namespace members.
void sub3() {
j = 2; // legal - Blue::j
print(j); // legal - Blue::print()
int j = 10; // legal - local j hides Blue::j
Blue::j = 5; // must qualify
. . .
}
A local declaration for j hides the Blue namespace member.
Consequently, a qualification is necessary to access j from namespace
Blue.
Local Using Directives
Local using directives appear inside blocks, functions, and namespace
definitions. Here's an example of a function with a local using
directive.
namespace Blue { // define namespace Blue
int j; // j is a member of namespace Blue
void print(int); // print() is a member of namespace Blue
}
void sub1() {
using namespace Blue; // local using directive
j = 0; // legal - Blue::j
print(j); // legal - Blue::print()
. . .
}
void sub2() {
j = 1; // illegal - j not defined
print(j); // illegal - print() not defined
. . .
}
The local using directive provides access to j and print()
from namespace Blue only within sub1(). Inside sub2(),
j and print() generate compilation errors because no local
using directive provides access.
NOTE
The scope of a local using directive is valid only inside the block where it
appears.
Potential conflicts may occur with global and local using directives when
members of different namespaces have the same name or match global names. The
compiler flags the ambiguity when you use the name, not at the using directive.
Here's an example with local using directives.
namespace Black { // define namespace Black
int j;
void print(int);
char ch;
}
namespace White { // define namespace White
int j;
void print(int);
double vision;
}
int j; // global j
void sub3() {
using namespace Black; // local using directive for Black
using namespace White; // local using directive for White
j = 0; // illegal - Black::j, White::j, or ::j?
print(5); // illegal - Black or White print()?
ch = 'a'; // legal - Black::ch
vision = 7.65; // legal - White::vision
int j = 10; // local j, hides all others
::j = 5; // legal - global j
White::j = 5; // legal - White::j from White
Black::print(j); // legal - Black::print(), local j
}
The local using directives for namespaces Black and White make unqualified
access to j and print() ambiguous. Access to variables
ch and vision are not ambiguous, however. Note that the local
j in sub3() makes qualification necessary to access the global
j and the j member in White.
Local using directives are also legal inside namespace definitions. These
using directives are transitive, as shown in the following example.
namespace Black { // define namespace Black
int j;
void print(int);
char ch;
}
namespace Gray { // define namespace Gray
using namespace Black; // local using directive
int j; // separate from Black::j
double talk;
}
namespace Black { // namespace extension
bool contrast;
}
void sub3() {
using namespace Gray; // local using directive
print(5); // legal - Black::print()
ch = 'a'; // legal - Black::ch
talk = 5.67; // legal - Gray::talk
j = 22; // illegal - Black::j or Gray::j?
Black::j = 22; // legal - Black::j
contrast = true; // legal - Black::contrast
. . .
}
Namespace Gray contains a local using directive for namespace Black, making
Black members j, print(), and ch available inside
Gray. Note that Gray::j and Black::j are separate namespace
members. Without a qualification inside sub3(), the compiler reports
ambiguity errors in the assignment to j.
A namespace extension adds contrast to the Black namespace, but
contrast is not accessible inside Gray. All of Black's members are
accessible in sub3() because the namespace extension for Black appears
before the local using directive.
Using Declarations
Using directives make all namespace members accessible without qualification.
In contrast, using declarations qualify only individual namespace members. Using
declarations are declarations and, as such, create declarations in the current
scope. Here is the format.
using namespace_name::member_name;
The previously defined namespace qualifier namespace_name follows
the keyword using. member_name must be declared inside
namespace_name. Using declarations may appear at global scope and local
scope (inside blocks, functions, and namespace definitions).
Here are several examples of using declarations.
namespace Black { // define namespace Black
int j;
void print(int);
char ch;
}
namespace White { // define namespace White
int j;
void print(int);
double vision;
}
using White::vision; // global using declaration
void sub1() {
using Black::print; // local using declaration
ch = 'a'; // illegal - ch not defined
vision = 7.65; // legal - White::vision
print(5); // legal - Black::print()
. . .
}
void sub2() {
print(5); // illegal - print() not defined
vision = 3.45; // legal - White::vision
. . .
}
A global using declaration for White::vision makes this namespace
member accessible to both sub1() and sub2() without
qualification. The local using declaration brings Black::print() into
local scope for only sub1(). Invoking print(5), therefore,
calls Black::print(5) in sub1() but is illegal in
sub2().
NOTE
Be careful when mixing using directives and using declarations in the same
scope.
void sub3() {
using namespace White; // local using directive
using Black::print; // local using declaration
print(5); // legal - Black::print()
j = 2; // legal - White::j
using Black::j; // local using declaration
j = 3; // legal - Black::j;
int j = 10; // illegal - j already defined
. . .
}
The local using directive inside sub3() makes all of White's
members accessible. The local using declaration makes print() the only
member accessible from Black. The assignment j=2 uses White::j
because Black::j is not in local scope at this point. When we bring
Black::j into local scope with a using declaration, subsequent
assignments modify the j member in Black. The declaration for
j is illegal because it is already in local scope.
Using declarations are also convenient with libraries from different sources.
Consider the following math_lib and geo_lib namespaces, which both define Point
but have different initial values for their origin members.
namespace math_lib { // Math Library
struct Point { double x, y; };
Point origin = { 0, 0 };
double slope(Point, Point);
double max(double a, double b);
int max(int a, int b);
}
namespace geo_lib { // Geometry Library
struct Point { double x, y; };
struct Line { . . . };
struct Angle { . . . };
Point origin = { 10, 10 };
}
Using declarations specify which namespace members a sub1() function
may access.
math_lib::Point origin = { 5, 5 }; // global declaration
void sub1() {
using math_lib::Point; // Point from math_lib
using geo_lib::Line; // Line from geo_lib
using geo_lib::origin; // origin from geo_lib
using math_lib::slope; // slope() from math_lib
using math_lib::origin ; // error, multiple declaration
cout << origin.x << endl; // legal - geo_lib::origin
cout << ::origin.x << endl; // legal - global origin
. . .
}
The global declaration for origin uses type Point from math_lib.
Inside sub1(), local using declarations specify which members we want
from each namespace. The using declaration for math_lib::origin is
illegal because origin is already in local scope from namespace
geo_lib. Note that the scope operator is necessary to access the global
origin in the last cout statement. Otherwise, we refer to the
origin member in geo_lib.
NOTE
Using declarations specify names without type information. The following
using declaration, for instance, brings both max() functions from
namespace math_lib into the local scope of sub2().
void sub2() {
using math_lib::max; // all max functions
cout << max(10, 20) << endl; // max(int, int)
cout << max(5.6, 7.8) << endl; // max(double, double)
}
When more than one function has the same name, all functions with that name
are brought into local scope (see "Overloading and Namespaces" on page
286).
Namespace Aliases
Namespace aliases create synonym names for namespaces. You'll want to
use aliases when a namespace has a long name. Here's the format.
namespace alias_name = namespace_name;
Follow the keyword namespace with your alias (alias_name).
namespace_name must be a previously defined namespace. You can use more
than one alias for the same namespace qualifier, but you can't alias an
existing alias. Aliases apply only to the translation unit where they appear;
the linker sees the original name.
Namespace aliases are convenient shorthand names.
namespace A_Very_Long_Library_Name {
struct Point { double x, y; };
Point origin = { 10, 10 };
}
namespace ALN = A_Very_Long_Library_Name; // alias
void sub() {
using ALN::Point; // using declaration
Point first = ALN::origin; // namespace member
. . .
}
Namespace aliases may appear in using directives, using declarations, and
qualified namespace members.
Namespaces with Program Development
Let's put namespaces to work in a complete program. The following
geometry.h header file defines a namespace for a geometry library.
Listing 3.14 geometry.h definitions
#ifndef GEOMETRYH
#define GEOMETRYH
// geometry.h - geometry definitions
namespace Geometry { // namespace definition
struct Point {
double x, y;
};
double slope(Point, Point);
}
#endif
Namespace Geometry includes a Point type and a slope() function.
Additional definitions appear in geometry.C.
Listing 3.15 geometry.C implementations
// geometry.C - geometry implementations
#include "geometry.h"
namespace Geometry { // namespace extension
Point origin = { 0, 0 };
}
double Geometry::slope(Point a1, Point a2) {
double dy = a2.y - a1.y;
double dx = a2.x - a1.x;
if (dx == 0)
throw "slope(): undefined slope";
return dy / dx;
}
A namespace extension adds an origin member to Geometry. The
slope() function calculates slopes from Point values and throws
character string exceptions if the slope denominator is zero.
Here's an application that uses this Geometry namespace to calculate
slopes for Point variables.
Listing 3.16 mymath3.C geometry application
// mymath3.C - namespaces with program development
#include <iostream.h>
#include "geometry.h"
namespace Geo = Geometry; // alias
using Geo::Point; // using declaration
using Geo::slope; // using declaration
namespace { // unnamed namespace
Point origin = { 10, 10 };
}
int main()
{
try {
Point a = { 3, 5 };
Point b = { 6, 10 };
cout << "Line a_b has slope " << slope(a, b) << endl;
cout << "Line origin_a has slope " << slope(origin, a) << endl;
}
catch (char *msg) { // catch handler
cout << msg << endl;
return 1;
}
return 0;
}
$ CC mymath3.C geometry.C -o mymath3
$ mymath3
Line a_b has slope 1.66667
Line origin_a has slope 0.714286
The program creates a Geo alias for the Geometry namespace qualifier. Global
using declarations use alias Geo to bring Point and slope() from the
Geometry namespace to global scope. An unnamed namespace defines a global
origin that does not conflict with the origin member in
Geometry. Inside the try block, we define Point variables a and
b before calculating their slopes. The catch handler displays error
messages from character string exceptions thrown by slope().