Home > Articles > Programming > C/C++

C++ Reference Guide

Hosted by

Properties of Namespaces

Last updated Jan 1, 2003.

Namespaces are more than just directories or containers of names. In the following passages, I will discuss some of the namespaces' features and their use.

A Fully Qualified Name

A namespace is a scope in which declarations and definitions are grouped together. In order to refer to any of these from another scope, a fully qualified name is required. A fully qualified name has the form: namespace::classname::identifier. Since both namespaces and classes can be nested, the resulting name can be rather long but it ensures uniqueness:

unsigned int maxlen = std::string::npos;

If repeating the fully qualified name seems tedious, you can use a <I>using declaration</i> or a <I>using directive</i> instead.

A using-Declaration and a using-Directive

A using-declaration consists of the keyword using followed by a namespace::member. It instructs the compiler to locate every occurrence of a certain namespace member (type, operator, function, constant, etc.) in the specified namespace, as if the fully-qualified name were used. For example:

#include <vector> //STL vector; defined in namespace std
int main()
{
 // the following is a using declaration; every
 //occurrence of vector is looked up in std
 using std::vector; 
 vector <int> vi; 
}

A using-directive, on the other hand, renders all the names of a certain namespace accessible in the directive's scope. For example:

#include <vector> // belongs to namespace std
#include <iostream> // also in namespace std
int main()
{
 using namespace std; // a using directive
 //all <iostream> and <vector> declarations 
 //can now be accessed without 
 //fully qualified names
 vector <int> vi;
 vi.push_back(10);
 cout<<vi[0];
}

Let's look back at our string class example:

//file excelSoftCompany.h
namespace excelSoftCompany 
{  
 class string {/*..*/};
 class vector {/*..*/};
}

You can access your own string class as well as the standard string class in the same program without risking name clashes like this:

#include <string> // std::string
#include "excelSoftCompany.h"

int main()
{
 using namespace excelSoftCompany;
 string s; // referring to class excelSoftCompany::string
 std::string standardstr; // ANSI string 
}

Namespace Aliases

Choosing a short name for a namespace can eventually lead to a name clash -- the very same problem we wanted to avoid in the first place. However, very long namespaces are tiresome. For this purpose, you can use a namespace alias. In the following example, I define an alias called ESC for the unwieldy Excel_Software_Company namespace:

//file decl.h
namespace Excel_Software_Company 
{
 class Date {/*..*/};
 class Time {/*..*/};
}

//file calendar.cpp
#include "decl.h"
int main()
{
 namespace ESC = Excel_Software_Company; //alias
 ESC::Date date;
 ESC::Time time;
}

Argument-Dependent Lookup

Andrew Koenig devised an algorithm known as Koenig lookup for resolving namespace members. This algorithm, also called argument-dependent lookup, is used in all standard-conforming compilers to handle cases like the following:

namespace MYNS
{
 class C {};
 void func(C);
}

MYNS::C c; // global object of type MYNS::C

int main()
{
 func(c); // OK, MYNS::f called
}

Neither a using-declaration nor a using-directive appears in the program. And yet, the compiler did the right thing -- it correctly identified the unqualified name func as the function declared in namespace MYNS by applying Koenig lookup. How does it work? Koenig lookup instructs the compiler to look at not just the usual places such as the local scope, but also the namespace that contains the argument's type. Thus, in the following source line, the compiler detects that the object c, which is the argument of the function func, belongs to namespace MYNS. Consequently, it looks at namespace MYNS to locate the declaration of func, "guessing" the programmer's intent.

func(c); // OK, MYNS::f called

Without Koenig lookup, namespaces would impose an unacceptable burden on the programmer, who would have to repeatedly specify the fully qualified names, or instead, use numerous using-declarations. To push the argument in favor of Koenig lookup even further, consider the following example:

#include<iostream>
using std::cout;

int main()
{
 cout<<"hi"; //OK, operator << 
       //was brought into scope 
       //by Koenig lookup
}

The using declaration injects std::cout into the scope of main(), thereby enabling the programmer to use the non-qualified name cout. However, the overloaded << operator, as you may recall, is not a member of std::cout. Rather, it's declared in namespace std as an external function that takes a std::ostream object as its argument. Without Koenig lookup, the programmer would have to write something like this:

std::operator<<(cout, "hi");

Fortunately, Koenig lookup "does the right thing" and saves the programmer from this tedium in an elegant way.