Namespaces

Why Namespaces Exist (Name Collisions)

A namespace is a named space in a program, containing a group of related identifiers (functions, variables, types, etc.). Everything that is declared inside a namespace is part of that namespace and is isolated from other identifiers with the same name in other namespaces or in the global namespace. The main reason for this is to avoid namespace conflicts.

As you move beyond a handful of functions in your code, there's something subtle, but deeply irritating, that will tend to occur. Names that at one time were unique will cease to be. Your compiler and linker will bemoan the fact that an identifier was used twice and refuse to build. This is the pain that namespaces remove.

Just a simple program that computes the area of a rectangle and displays it:

#include <iostream>

int calculateArea(int length, int width)
{
    return length * width;
}

int main()
{
    int area{ calculateArea(5, 4) };
    std::cout << "Area is " << area << '\n';
    return 0;
}

The function calculateArea takes two int parameters, multiplies them, and returns their value. In main we call it with the values 5 and 4, put the value it returns into area, and then print out area. If the only occurrence of the name calculateArea was in the function prototype, everything would work perfectly: (no conflicts)

Now think about the program expanding. You have a second programmer (or you, a week later) introducing a function to find the surface area of a box. It will need a name of course, and it just feels right for it to look like this:

#include <iostream>

int calculateArea(int length, int width)
{
    return length * width;
}

int calculateArea(int length, int width, int height)
{
    return 2 * (length * width + length * height + width * height);
}

int main()
{
    int area{ calculateArea(5, 4) };
    std::cout << "Area is " << area << '\n';
    return 0;
}

This program will not compile. The compiler identifies two different functions named calculateArea. While these functions have different parameter lists, they are ultimately sharing the same name.

Both declare a function named calculateArea within the global namespace, and cannot do so.

The global namespace is where all the names in a program reside that are declared outside of any other scope.

This breaks down further when we want to split code into different files. Imagine we have a file, where:

// helpers.cpp
#include <iostream>

void print()
{
    std::cout << "Helper message\n";
}

And an additional file attempts to declare its own version of the identical name:

// main.cpp
#include <iostream>

void print()
{
    std::cout << "Main message\n";
}

int main()
{
    print();
    return 0;
}

Since the linker attempts to combine the object files, it encounters two definitions of print. This causes a linker error about multiple definitions of the same symbol. Since the global namespace is unique across the whole program, identical names cannot reside alongside one another even if they are in different source files.

Namespaces provide us with an escape hatch. Because each namespace is its own named region, names declared inside one namespace do not automatically clash with names declared inside of another namespace or in the global namespace. We form a namespace by writing namespace followed by a name and then a pair of curly braces.

#include <iostream>

namespace Geometry
{
    int calculateArea(int length, int width)
    {
        return length * width;
    }
}

namespace Physics
{
    int calculateArea(int length, int width, int height)
    {
        return 2 * (length * width + length * height + width * height);
    }
}

int main()
{
    int rectangle{ Geometry::calculateArea(5, 4) };
    int box{ Physics::calculateArea(5, 4, 3) };
    std::cout << "Rectangle area: " << rectangle << '\n';
    std::cout << "Box surface area: " << box << '\n';
    return 0;
}

So, within the namespace Geometry we put one version of calculateArea which takes two parameters. Within the namespace Physics we put a different version which takes three parameters. Now that we have these two functions in different namespaces we can write them both in our program without the compiler getting confused.

However, when we call one of the functions we need to specify the namespace.

We do this by writing PathName::functionName. 

So in our case we would write: Geometry::calculateArea or Physics::calculateArea.

This can be applied to multiple files as well. For example, one file can add helpers into namespace1, while another file can put the other helpers into namespace2, and both names are kept unique. The world is no longer doomed to have the global namespace filled with all the identifiers ever created by the program.

Name collisions are real. They occur as soon as two separate pieces of code arbitrarily select the same name for a function, a variable, or a type. The global namespace does nothing to prevent that accident from happening. Namespaces are there so that each group of related names can set up their own safe area and prevent collisions altogether.

0 comments

No comments yet. Be the first.

Add a comment

About CppLearn

CppLearn is an interactive, free reference for modern C++. In each lesson, you'll find out how the language works, rather than just lines of code that you can copy and paste. We cover concepts starting from beginner lessons on fundamentals and proceed to templates, move semantics, smart pointers, and areas that usually trap or scare C++ learners.