Namespaces

"The road to hell is paved with global variables" -- Steve McConnell

Namespaces

A simple program with 3 global symbols:
#include <iostream>  // cout, endl

int foo = 1;         // foo in global namespace
int bar = 2;         // bar in global namespace

int Div2(int value) // Div2 in global namespace
{
  return value / 2;
}

int main()
{
  std::cout << foo << std::endl;     // use global foo
  std::cout << bar << std::endl;     // use global bar
  std::cout << Div2(8) << std::endl; // use global Div2
  return 0;
}
However, placing these symbols in a namespace will prevent the program from compiling:
int main()
{
  std::cout << foo << std::endl;     // error, foo is undefined
  std::cout << bar << std::endl;     // error, bar is undefined
  std::cout << Div2(8) << std::endl; // error, Div2 is undefined
  return 0;
}
We need to qualify the symbols in the namespace:
int main()
{
  std::cout << IntroCppProgramming::foo << std::endl;     
  std::cout << IntroCppProgramming::bar << std::endl;     
  std::cout << IntroCppProgramming::Div2(8) << std::endl; 
  return 0;
}
The general form of a namespace definition is:
namespace user-defined-name
{
  declaration/definition
  declaration/definition
  ...
}

Unnamed Namespaces

What happens when we run out of unique names for namespaces?
#include <iostream> // cout, endl

namespace
{
  double sqrt(double x) { return x; }
}

int main()
{
  std::cout << sqrt(25.0) << std::endl;   // No qualification needed
  return 0;
}
If we have a symbol in an unnamed namespace that is the same as a global symbol in our program, we won't be able to access the symbol in the unnamed namespace.
#include <iostream> // cout, endl
#include <cmath>    // sqrt

namespace
{
  double sqrt(double x) { return x; };
}

double sqrt(double x) { return x; }; // global 

int main()
{
  std::cout << ::sqrt(25.0) << std::endl;      // Global sqrt function defined in this file
  std::cout << std::sqrt(25.0) << std::endl;   // sqrt from std namespace
  std::cout << sqrt(25.0) << std::endl;        // Line 15: Ambiguous (from global or unnamed namespace?)
  
  return 0;
}
These are the error messages from the GNU compiler:
sqrt.cpp: In function 'int main()':
sqrt.cpp:15: error: call of overloaded 'sqrt(double)' is ambiguous
sqrt.cpp:9: note: candidates are: double sqrt(double)
sqrt.cpp:6: note:  double::sqrt(double)

Class Design Tip
When hiding symbols at the file scope, prefer to use unnamed namespaces over the already-overloaded-too-much C static keyword.

Scope Resolution Operator

Example:
#include <iostream> // cout, endl

int foo = 1; // global
int bar = 2; // global

void fn1()
{
  int foo = 10;    // local foo #1 hides global foo
  int bar = foo;   // local bar #1 hides global bar (set to local foo)
  int baz = ::foo; // local baz #1 is set to global foo

  if (bar == 10)   // local bar #1
  {
    int foo = 100; // local foo #2 hides local #1 and global
    bar = foo;     // local bar #1 is set to local foo #2
    foo = ::bar;   // local foo #2 is set to global bar
  }

  ::foo = foo;   // global foo is set to local foo #1
  ::bar = ::foo; // global bar is set to global foo

  std::cout << "foo is " << foo << std::endl;     // local foo #1 is 10
  std::cout << "bar is " << bar << std::endl;     // local bar #1 is 100
  std::cout << "::foo is " << ::foo << std::endl; // global foo is 10
  std::cout << "::bar is " << ::bar << std::endl; // global bar is 10
}
Marked-up diagram

Notes:

Namespace Aliases

Given these namespaces:
namespace AdvancedProgramming 
{
  int foo = 11;    
  int bar = 12;    
  int f1(int x)  { return x / 2; }
}

namespace IntroductoryProgramming
{
  int foo = 21;    
  int bar = 22;    
  int Div2(int x) {return x / 2; }
}

using them requires a lot of typing:
int main()
{
  std::cout << AdvancedProgramming::foo << std::endl;
  std::cout << IntroductoryProgramming::Div2(8) << std::endl;

  return 0;
}
To allow unique namespaces and to shorten the names, you can create a namespace alias
  // Declare these after the namespace definitions above
namespace AP = AdvancedProgramming;
namespace IP = IntroductoryProgramming;

int main()
{
    // Now, use the shorter aliases
  std::cout << AP::foo << std::endl;
  std::cout << IP::foo << std::endl;
  
  std::cout << AP::f1(8) << std::endl;
  std::cout << IP::Div2(8) << std::endl;

  return 0;
}

Design Tip
Don't create very terse namespaces (like std). Create unique and meaningful namespaces and let the user create shorthand notation with aliases.

Using Directives

A using directive allows you to make all of the names in a namespace visible at once:

More detailed example:
namespace Stuff
{
  int foo = 11;       // Stuff::foo     
  int bar = 12;       // Stuff::bar         
  int baz = 13;       // Stuff::baz     
}

void f1()
{
  int foo = 3;        // local, hides nothing
  int x = Stuff::foo; // OK
  int y = bar;        // error, bar is unknown
}

int foo = 20;         // global ::foo 

int main()
{
  using namespace Stuff;               // Stuff's members are now accessible without Stuff::
                                         //  qualifier within main
  std::cout << ::foo << std::endl;      // no problem, global
  std::cout << Stuff::foo << std::endl; // no problem, Stuff::foo
  std::cout << foo << std::endl;        // error, foo is ambiguous (global ::foo or Stuff::foo?) 

  std::cout << bar << std::endl;  // Stuff::bar
  std::cout << baz << std::endl;  // Stuff::baz

  int foo = 3;        // OK, hides Stuff::foo and global ::foo 
  int x = Stuff::foo; // OK, use qualified name
  x = foo;            // OK, local foo above
  x = ::foo;          // OK, global foo

  return 0;
}

Summary:

Using directives were designed for backward-compatibility with existing C++ code (which doesn't understand namespaces) to help support older code. They should be used cautiously when writing new code, especially if they are used at a global scope.

The Standard Namespace (and using declarations)

Now that we've seen some of the details of how namespaces are created and used, we can see how they can be applied.

Now we can write code like this (to ensure job security):
#include <iostream> // For cout and endl;

int main()
{
  int cout = 16;  // cout is an int
  cout << 1;      // no effect
  std::cout << cout << 3 << std::endl;   // std::cout is a stream, prints: 163
  std::cout << (cout << 3) << std::endl; // std::cout is a stream, prints: 128
  return 0;
}
Operator precedence chart for C++

Self check You should understand why these examples are legal or illegal.


Understanding the Big Picture™

  1. What is the purpose of namespaces in C++? In other words, what problem from C was solved by inventing them?
  2. Are there times when namespaces aren't very useful? When?