MFC Programmer's SourceBook : Thinking in C++
Bruce Eckel's Thinking in C++, 2nd Ed Contents | Prev | Next

Namespaces

Although names can be nested inside classes, the names of global functions, global variables, and classes are still in a single global name space. The static keyword gives you some control over this by allowing you to give variables and functions internal linkage (make them file static). But in a large project, lack of control over the global name space can cause problems. To solve these problems for classes, vendors often create long complicated names that are unlikely to clash, but then you’re stuck typing those names. (A typedef is often used to simplify this.) It’s not an elegant, language-supported solution.

You can subdivide the global name space into more manageable pieces using the namespace feature of C++. [28] The namespace keyword, like class, struct, enum, and union, puts the names of its members in a distinct space. While the other keywords have additional purposes, the creation of a new name space is the only purpose for namespace.

Creating a namespace

The creation of a namespace is notably similar to the creation of a class:

namespace MyLib {
  // Declarations
}
This produces a new namespace containing the enclosed declarations. There are significant differences with class, struct, union and enum, however:

  1. A namespace definition can only appear at the global scope, but namespaces can be nested within each other.
  2. No terminating semicolon is necessary after the closing brace of a namespace definition.
  3. A namespace
definition can be “continued” over multiple header files using a syntax that would appear to be a redefinition for a class:

//: C10:Header1.h
namespace MyLib {
  extern int x;
  void f();
  // ...
} ///:~
//: C10:Header2.h
// Add more names to MyLib
namespace MyLib { // NOT a redefinition!
  extern int y;
  void g();
  // ...
} ///:~ 

  1. A namespace
name can be aliased to another name, so you don’t have to type an unwieldy name created by a library vendor:

namespace BobsSuperDuperLibrary {
  class Widget { /* ... */ };
  class Poppit { /* ... */ };
  // ...
}
// Too much to type! I’ll alias it:
namespace Bob = BobsSuperDuperLibrary; 

  1. You cannot create an instance of a namespace as you can with a class.

Unnamed namespaces

Each translation unit contains an unnamed namespace that you can add to by saying namespace without an identifier:

namespace {
  class Arm  { /* ... */ };
  class Leg  { /* ... */ };
  class Head { /* ... */ };
  class Robot {
    Arm arm[4];
    Leg leg[16];
    Head head[3];
    // ...
  } xanthan;
  int i, j, k;
}

The names in this space are automatically available in that translation unit without qualification. It is guaranteed that an unnamed space is unique for each translation unit. If you put local names in an unnamed namespace, you don’t need to give them internal linkage by making them static.

Friends

You can inject a friend declaration into a namespace by declaring it within an enclosed class:

namespace me {
  class Us {
    //...
    friend you();
  };
}

Now the function you( ) is a member of the namespace me.

Using a namespace

You can refer to a name within a namespace in two ways: one name at a time, using the scope resolution operator, and more expediently with the using keyword.

Scope resolution

Any name in a namespace can be explicitly specified using the scope resolution operator, just like the names within a class:

namespace X {
  class Y {
    static int i;
  public:
    void f();
  };
  class Z;
  void func();
}
int X::Y::i = 9; 
class X::Z {
  int u, v, w;
public:
  Z(int i);
  int g();
};
X::Z::Z(int i) { u = v = w = i; }
int X::Z::g() { return u = v = w = 0; } 
void X::func() {
  X::Z a(1);
  a.g();
}

So far, namespaces look very much like classes.

The using directive

Because it can rapidly get tedious to type the full qualification for an identifier in a namespace, the using keyword allows you to import an entire namespace at once. When used in conjunction with the namespace keyword, this is called a using directive. The using directive declares all the names of a namespace to be in the current scope, so you can conveniently use the unqualified names:

namespace math {
  enum sign { positive, negative };
  class Integer {
    int i;
    sign s;
  public:
    Integer(int ii = 0) 
      : i(ii),
        s(i >= 0 ? positive : negative)
    {}
    sign getSign() { return s; }
    void setSign(sign sgn) { s = sgn; }
    // ...
  };
  integer a, b, c;
  integer divide(integer, integer);
  // ...
}

Now you can declare all the names in math inside a function, but leave those names nested within the function:

void arithmetic() {
  using namespace math;
  integer x;
  x.setSign(positive);
}

Without the using directive, all the names in the namespace would need to be fully qualified.

One aspect of the using directive may seem slightly counterintuitive at first. The visibility of the names introduced with a using directive is the scope where the directive is made. But you can override the names from the using directive as if they’ve been declared globally to that scope!

void q() {
  using namespace math;
  integer A; // Hides math::A;
  A.setSign(negative);
  math::A.setSign(positive);
}

If you have a second namespace:

namespace calculation {
  class Integer {};
  integer divide(integer, integer);
  // ...
}

And this namespace is also introduced with a using directive, you have the possibility of a collision. However, the ambiguity appears at the point of use of the name, not at the using directive:

void s() {
  using namespace math;
  using namespace calculation;
  // Everything’s ok until:
  divide(1, 2); // Ambiguity
}

Thus it’s possible to write using directives to introduce a number of namespaces with conflicting names without ever producing an ambiguity.

The using declaration

You can introduce names one at a time into the current scope with a using declaration. Unlike the using directive, which treats names as if they were declared globally to the scope, a using declaration is a declaration within the current scope. This means it can override names from a using directive:

namespace U {
  void f();
  void g();
}
namespace V {
  void f();
  void g();
}
void func() {
  using namespace U; // Using directive
  using V::f; // Using declaration
  f(); // Calls V::f();
  U::f(); // Must fully qualify to call
}

The using declaration just gives the fully specified name of the identifier, but no type information. This means that if the namespace contains a set of overloaded functions with the same name, the using declaration declares all the functions in the overloaded set.

You can put a using declaration anywhere a normal declaration can occur. A using declaration works like a normal declaration in all ways but one: it’s possible for a using declaration to cause the overload of a function with the same argument types (which isn’t allowed with normal overloading). This ambiguity, however, doesn’t show up until the point of use, rather than the point of declaration.

A using declaration can also appear within a namespace, and it has the same effect as anywhere else: that name is declared within the space:

namespace Q {
  using U::f;
  using V::g;
  // ...
}
void m() {
  using namespace Q;
  f(); // Calls U::f();
  g(); // Calls V::g();
}

A using declaration is an alias, and it allows you to declare the same function in separate namespaces. If you end up redeclaring the same function by importing different namespaces, it’s OK – there won’t be any ambiguities or duplications.


[28] Your compiler may not have implemented this feature yet; check your local documentation.

Contents | Prev | Next


Go to CodeGuru.com
Contact: webmaster@codeguru.com
© Copyright 1997-1999 CodeGuru