DekGenius.com
Team LiB   Previous Section   Next Section

2.7 Namespaces

A namespace is a named scope. By grouping related declarations in a namespace, you can avoid name collisions with declarations in other namespaces. For example, suppose you are writing a word processor, and you use packages that others have written, including a screen layout package, an equation typesetting package, and an exact-arithmetic package for computing printed positions to high accuracy with fixed-point numbers.

The equation package has a class called fraction, which represents built-up fractions in an equation; the arithmetic package has a class called fraction, for computing with exact rational numbers; and the layout package has a class called fraction for laying out fractional regions of a page. Without namespaces, all three names would collide, and you would not be able to use more than one of the three packages in a single source file.

With namespaces, each class can reside in a separate namespace—for example, layout::fraction, eqn::fraction, and math::fraction.

C++ namespaces are similar to Java packages, with a key difference: in Java, classes in the same package have additional access rights to each other; in C++, namespaces confer no special access privileges.

2.7.1 Namespace Definitions

Define a namespace with the namespace keyword followed by an optional identifier (the namespace name) and zero or more declarations in curly braces. Namespace declarations can be discontiguous, even in separate source files or headers. The namespace scope is the accumulation of all definitions of the same namespace that the compiler has seen at the time it looks up a given name in the namespace. Namespaces can be nested. Example 2-15 shows a sample namespace definition.

Example 2-15. Defining a namespace
// The initial declaration
namespace numeric {
  class rational { ... }
  template<typename charT, typename traits>
  basic_ostream<charT,traits>& operator<<(
    basic_ostream<charT,traits>& out, const rational& r);
}

 . . . 

// This is a second definition. It adds an operator  to the namespace.
namespace numeric {
  rational operator+(const rational&, const rational&);
}

// The definition of operator+ can appear inside or outside the namespace
// definition. If it is outside, the name must be qualified with the scope
// operator.
numeric::rational numeric::operator+(const rational& r1,
                                     const rational& r2)
{
   . . . 
}

int main(  )
{
  using numeric::rational;
  rational a, b;
  std::cout << a + b << '\n';
}

You can define a namespace without a name, in which case the compiler uses a unique, internal name. Thus, each source file's unnamed namespace is separate from the unnamed namespace in every other source file.

You can define an unnamed namespace nested within a named namespace (and vice versa). The compiler generates a unique, private name for the unnamed namespace in each unique scope. As with a named namespace, you can use multiple namespace definitions to compose the unnamed namespace, as shown in Example 2-16.

Example 2-16. Unnamed namespaces
#include <iostream>
#include <ostream>

namespace {
  int i = 10;
}

namespace {
  int j;          // Same unnamed namespace
  namespace X {
    int i = 20;   // Hides i in outer, unnamed namespace
  }
  namespace Y = X;
  int f(  ) { return i; }
}

namespace X {
  int i = 30;
  // X::unnamed is different namespace than ::unnamed.
  namespace {
    int i = 40;  // Hides ::X::i, but is inaccessible outside the unnamed
                 // namespace
    int f(  ) { return i; }
  }
}

int main(  )
{
  int i = X::i;  // ambiguous: unnamed::X or ::X?
  std::cout << ::X::f(  ) << '\n'; // Prints 40
  std::cout << Y::i << '\n';       // Prints 20
  std::cout << f(  ) << '\n';      // Prints 10
}

The advantage of using an unnamed namespace is that you are guaranteed that all names declared in it can never clash with names in other source files. The disadvantage is that you cannot use the scope operator (::) to qualify identifiers in an unnamed namespace, so you must avoid name collisions within the same source file.

C programmers are accustomed to using global static declarations for names that are private to a source file. You can do the same in C++, but it is better to use an unnamed namespace because a namespace can contain any kind of declaration (including classes, enumerations, and templates), whereas static declarations are limited to functions and objects.

Declarations of static objects and functions at namespace scope are deprecated in C++.

Declarations outside of all namespaces, functions, and classes are implicitly declared in the global namespace. A program has a single global namespace, which is shared by all source files that are compiled and linked into the program. Declarations in the global namespace are typically referred to as global declarations. Global names can be accessed directly using the global scope operator (the unary ::), as described earlier in Section 2.3.1.

2.7.2 Namespace Aliases

A namespace alias is a synonym for an existing namespace. You can use an alias name to qualify names (with the :: operator) in using declarations and directives, but not in namespace definitions. Example 2-17 shows some alias examples.

Example 2-17. Namespace aliases
namespace original {
  int f(  );
}

namespace = original;     // Alias

int ns::f(  ) { return 42; }   // OK
using ns::f;               // OK

int g(  ) { return f(  ); }

namespace ns { // Error: cannot use alias here
  int h(  );
}

A namespace alias can provide an abbreviation for an otherwise unwieldy namespace name. The long name might incorporate a full organization name, deeply nested namespaces, or version numbers:

namespace tempest_software_inc {
  namespace version_1 { ... }
  namespace version_2 { ... }
}
namespace tempest_1 = tempest_software_inc::version_1;
namespace tempest_2 = tempest_software_inc::version_2;

2.7.3 using Declarations

A using declaration imports a name from one namespace and adds it to the namespace that contains the using declaration. The imported name is a synonym for the original name. Only the declared name is added to the target namespace, which means using an enumerated type does not bring with it all the enumerated literals. If you want to use all the literals, each one requires its own using declaration.

Because a name that you reference in a using declaration is added to the current namespace, it might hide names in outer scopes. A using declaration can also interfere with local declarations of the same name.

Example 2-18 shows some examples of using declarations.

Example 2-18. using declarations
namespace numeric {
  class fraction {  . . .  };
  fraction operator+(int, const fraction&);
  fraction operator+(const fraction&, int);
  fraction operator+(const fraction&, const fraction&);
}

namespace eqn {
  class fraction {  . . .  };
  fraction operator+(int, const fraction&);
  fraction operator+(const fraction&, int);
  fraction operator+(const fraction&, const fraction&);
}

int main(  )
{
  numeric::fraction nf;
  eqn::fraction qf;

  nf = nf + 1;           // OK: calls numeric::operator+
  qf = 1 + qf;           // OK: calls eqn::operator+
  nf = nf + qf;          // Error: no operator+

  using numeric::fraction;
  fraction f;            // f is numeric::fraction
  f = nf + 2;            // OK
  f = qf;                // Error: type mismatch
  using eqn::fraction;   // Error: like trying to declare
                         // fraction twice in the same scope
  if (f > 0) {
    using eqn::fraction; // OK: hides outer fraction
    fraction f;          // OK: hides outer f
    f = qf;              // OK: same types
    f = nf;              // Error: type mismatch
  }
  int fraction;          // Error: name fraction in use
}

You can copy names from one namespace to another with a using declaration. Suppose you refactor a program and realize that the numeric::fraction class has all the functionality you need in the equation package. You decide to use numeric::fraction instead of eqn::fraction, but you want to keep the eqn interface the same. So you insert using numeric::fraction; in the eqn namespace.

Incorporating a name into a namespace with a using declaration is not quite the same as declaring the name normally. The new name is just a synonym for the original name in its original namespace. When the compiler searches namespaces under argument-dependent name lookup, it searches the original namespace. Example 2-19 shows how the results can be surprising if you are not aware of the using declaration. The eqn namespace declares operator<< to print a fraction, but fraction is declared in the numeric namespace. Although eqn uses numeric::fraction, when the compiler sees the use of operator<<, it looks in only the numeric namespace, and never finds operator<<.

Example 2-19. Creating synonym declarations with using declarations
namespace eqn {
  using numeric::fraction;
  // Big, ugly declaration for ostream << fraction
  template<typename charT, typename traits>
  basic_ostream<charT,traits>& operator<<(
    basic_ostream<charT,traits>& out, const fraction& f)
  {
    out << f.numerator(  ) << '/' << f.denominator(  );
    return out;
  }
}

int main(  )
{
  eqn::fraction qf;
  numeric::fraction nf;
  nf + qf;         // OK because the types are the same
  std::cout << qf; // Error: numeric namespace is searched for operator<<, but
                   // not eqn namespace
}

The using declaration can also be used within a class. You can add names to a derived class from a base class, possibly changing their accessibility. For example, a derived class can promote a protected member to public visibility. Another use of using declarations is for private inheritance, promoting specific members to protected or public visibility. For example, the standard container classes are not designed for public inheritance. Nonetheless, in a few cases, it is possible to derive from them successfully. Example 2-20 shows a crude way to implement a container type to represent a fixed-size array. The array class template derives from std::vector using private inheritance. A series of using declarations make selected members of std::vector public and keep those members that are meaningless for a fixed-size container, such as insert, private.

Example 2-20. Importing members with using declarations
template<typename T>
class array: private std::vector<T>
{
public:
  typedef T value_type;
  using std::vector<T>::size_type;
  using std::vector<T>::difference_type;
  using std::vector<T>::iterator;
  using std::vector<T>::const_iterator;
  using std::vector<T>::reverse_iterator;
  using std::vector<T>::const_reverse_iterator;

  array(std::size_t n, const T& x = T(  )) : std::vector<T>(n, x) {}
  using std::vector<T>::at;
  using std::vector<T>::back;
  using std::vector<T>::begin;
  using std::vector<T>::empty;
  using std::vector<T>::end;
  using std::vector<T>::front;
  using std::vector<T>::operator[];
  using std::vector<T>::rbegin;
  using std::vector<T>::rend;
  using std::vector<T>::size;
};

See Chapter 6 for more information about using declarations in class definitions.

2.7.4 using Directives

A using directive adds a namespace to the list of scopes that is used when the compiler searches for a name's declaration. Unlike a using declaration, no names are added to the current namespace. Instead, the used namespace is added to the list of namespaces to search right after the innermost namespace that contains both the current and used namespaces. (Usually, the containing namespace is the global namespace.) The using directive is transitive, so if namespace A uses namespace B, and namespace B uses namespace C, a name search in A also searches C. Example 2-21 illustrates the using directive.

Example 2-21. The using directive
#include <iostream>
#include <ostream>

namespace A {
  int x = 10;
}
namespace B {
  int y = 20;
}
namespace C {
  int z = 30;
  using namespace B;
}
namespace D {
  int z = 40;
  using namespace B;       // Harmless but pointless because D::y hides B::y
  int y = 50;
}

int main(  )
{
  int x = 60;
  using namespace A;       // Does not introduce names, so there is no conflict
                           // with x
  using namespace C;

  using namespace std;     // To save typing std::cout repeatedly

  cout << x << '\n';       // Prints 60 (local x)
  cout << y << '\n';       // Prints 20 
  cout << C::y << '\n';    // Prints 20
  cout << D::y << '\n';    // Prints 50

  using namespace D;
  cout << y << '\n'; // Error: y is ambiguous. It can be found in D::y and C's
                     // use of B::y.
}

2.7.5 How to Use Namespaces

Namespaces have no runtime cost. Don't be afraid to use them, especially in large projects in which many people contribute code and might accidentally devise conflicting names. The following are some additional tips and suggestions for using namespaces:

  • When you define a class in a namespace, be sure to declare all associated operators and functions in the same namespace.

  • To make namespaces easier to use, keep namespace names short, or use aliases to craft short synonyms for longer names.

  • Never place a using directive in a header. It can create name collisions for any user of the header.

  • Keep using directives local to functions to save typing and enhance clarity.

  • Use using namespace std outside functions only for tiny programs or for backward compatibility in legacy projects.

    Team LiB   Previous Section   Next Section