Ready to practice?
Sign up to access interactive coding exercises and track your progress.
Creating Custom Namespaces
Learn how to organize code with user-defined namespaces and the scope resolution operator to avoid naming conflicts.
User-defined namespaces and the scope resolution operator
In the Naming collisions and an introduction to namespaces lesson, we explored the concept of naming collisions and namespaces. As a quick reminder, a naming collision happens when two identical identifiers are introduced into the same scope, leaving the compiler unable to determine which one should be used. When this occurs, the compiler or linker produces an error due to insufficient information to resolve the ambiguity.
As codebases grow larger, the number of identifiers multiplies, dramatically increasing the likelihood of naming collisions. Since every identifier within a scope can potentially conflict with every other identifier in that same scope, a linear growth in identifiers leads to an exponential growth in potential collisions. This fundamental issue is why defining identifiers in the smallest possible scope is so important.
Let's examine a naming collision scenario and see how namespaces provide a solution. In this example, math_ops.cpp and text_ops.cpp contain functions with identical names and parameters but completely different behaviors.
math_ops.cpp:
// This process() multiplies its parameters
int process(int a, int b)
{
return a * b;
}
text_ops.cpp:
// This process() divides its parameters
int process(int a, int b)
{
return a / b;
}
main.cpp:
#include <iostream>
int process(int a, int b); // forward declaration for process
int main()
{
std::cout << process(20, 5) << '\n'; // which process will the linker use?
return 0;
}
When your project contains only math_ops.cpp or text_ops.cpp (but not both), everything compiles and runs smoothly. However, when you compile both files into the same program, you've introduced two distinct functions with identical names and parameters into the same scope (the global scope), causing a naming collision. The linker will report an error:
text_ops.cpp:3: multiple definition of `process(int, int)'; math_ops.cpp:3: first defined here
Notice that this error occurs at the point of redefinition, regardless of whether the process function is ever called.
You could resolve this by renaming one of the functions, but that would require updating all function calls throughout your code, which is tedious and error-prone. A superior approach is to place your functions into their own namespaces. This is precisely why the standard library exists within the std namespace.
Creating your own namespaces
C++ enables you to define your own namespaces using the namespace keyword. Namespaces you create in your programs are informally called user-defined namespaces (though program-defined namespaces would be more accurate).
The syntax for declaring a namespace looks like this:
namespace NamespaceIdentifier { // namespace content goes here }
You begin with the namespace keyword, followed by an identifier for your namespace, then curly braces containing the namespace's contents.
Traditionally, namespace names have used lowercase letters, and many style guides continue recommending this convention.
Advanced note: Some compelling reasons to start namespace names with a capital letter: (1) It follows the convention of starting program-defined types with a capital letter, maintaining consistency with qualified names like Bar::value; (2) It helps prevent naming collisions with lowercase system-provided names; (3) The C++20 standards document and C++ Core Guidelines adopt this style. We recommend starting namespace names with a capital letter, though both styles are acceptable.
A namespace must be defined either in the global scope or nested inside another namespace. Like function bodies, the contents of namespaces are conventionally indented one level. You might occasionally see an optional semicolon after a namespace's closing brace.
Here's how we can rewrite the previous example using namespaces:
math_ops.cpp:
namespace Math // define a namespace named Math
{
// This process() belongs to namespace Math
int process(int a, int b)
{
return a * b;
}
}
text_ops.cpp:
namespace Text // define a namespace named Text
{
// This process() belongs to namespace Text
int process(int a, int b)
{
return a / b;
}
}
Now the process() in math_ops.cpp resides in the Math namespace, while the process() in text_ops.cpp resides in the Text namespace. Let's see what happens when we recompile:
main.cpp:
int process(int a, int b); // forward declaration for process
int main()
{
std::cout << process(20, 5) << '\n'; // which process will the linker use?
return 0;
}
We now get a different error:
ConsoleApplication1.obj : error LNK2019: unresolved external symbol "int __cdecl process(int,int)" (?process@@YAHHH@Z) referenced in function _main
The compiler is satisfied (due to our forward declaration), but the linker cannot locate a definition for process in the global namespace. This is because both versions of process now exist within their respective namespace scopes, not in the global namespace.
There are two primary ways to specify which version of process() to use: the scope resolution operator or using statements (which we'll explore in a later lesson).
For clarity, we'll consolidate the following examples into single-file solutions.
Using the scope resolution operator (::) to access namespaces
The most direct way to tell the compiler to look in a specific namespace for an identifier is to use the scope resolution operator (::). The scope resolution operator instructs the compiler that the identifier specified by the right-hand operand should be found in the scope of the left-hand operand.
Here's an example using the scope resolution operator to explicitly specify we want the version of process() from the Math namespace:
#include <iostream>
namespace Math // define a namespace named Math
{
// This process() belongs to namespace Math
int process(int a, int b)
{
return a * b;
}
}
namespace Text // define a namespace named Text
{
// This process() belongs to namespace Text
int process(int a, int b)
{
return a / b;
}
}
int main()
{
std::cout << Math::process(20, 5) << '\n'; // use the process() from namespace Math
return 0;
}
This produces the expected output:
100
To use the version of process() from Text instead:
#include <iostream>
namespace Math // define a namespace named Math
{
// This process() belongs to namespace Math
int process(int a, int b)
{
return a * b;
}
}
namespace Text // define a namespace named Text
{
// This process() belongs to namespace Text
int process(int a, int b)
{
return a / b;
}
}
int main()
{
std::cout << Text::process(20, 5) << '\n'; // use the process() from namespace Text
return 0;
}
This produces:
4
The scope resolution operator is excellent because it allows us to explicitly specify which namespace to search, eliminating any potential ambiguity. We can even do this:
#include <iostream>
namespace Math // define a namespace named Math
{
// This process() belongs to namespace Math
int process(int a, int b)
{
return a * b;
}
}
namespace Text // define a namespace named Text
{
// This process() belongs to namespace Text
int process(int a, int b)
{
return a / b;
}
}
int main()
{
std::cout << Math::process(20, 5) << '\n'; // use the process() from namespace Math
std::cout << Text::process(20, 5) << '\n'; // use the process() from namespace Text
return 0;
}
This produces:
100 4
Using the scope resolution operator without a namespace prefix
The scope resolution operator can also be used before an identifier without specifying a namespace name (e.g., ::process). When used this way, the identifier (e.g., process) is looked up in the global namespace.
#include <iostream>
void display() // this display() lives in the global namespace
{
std::cout << " world\n";
}
namespace Greeting
{
void display() // this display() lives in the Greeting namespace
{
std::cout << "Hello";
}
}
int main()
{
Greeting::display(); // call display() in Greeting namespace
::display(); // call display() in global namespace (equivalent to just calling display() here)
return 0;
}
In this example, ::display() behaves identically to calling display() without scope resolution, making the scope resolution operator redundant here. However, the next example demonstrates a scenario where this syntax becomes useful.
How identifiers are resolved from within a namespace
When an identifier within a namespace is used without scope resolution, the compiler first attempts to find a matching declaration in that same namespace. If no match is found, the compiler checks each containing namespace in sequence, with the global namespace checked last.
#include <iostream>
void display() // this display() lives in the global namespace
{
std::cout << " world\n";
}
namespace Greeting
{
void display() // this display() lives in the Greeting namespace
{
std::cout << "Hello";
}
void displayMessage()
{
display(); // calls display() in Greeting namespace
::display(); // calls display() in global namespace
}
}
int main()
{
Greeting::displayMessage();
return 0;
}
This outputs:
Hello world
In the example above, display() is called without scope resolution. Since this call to display() occurs inside the Greeting namespace, the compiler first checks if Greeting::display() exists. Finding a match, it calls Greeting::display().
If Greeting::display() hadn't been found, the compiler would have checked the containing namespace (in this case, the global namespace) for a matching display().
Note that we also use the scope resolution operator without a namespace (::display()) to explicitly call the global version of display().
Forward declaring content within namespaces
In lesson 2.11 -- Header files, we discussed using header files to propagate forward declarations. For identifiers inside a namespace, those forward declarations must also be placed inside the same namespace:
multiply.h
#ifndef MULTIPLY_H
#define MULTIPLY_H
namespace Calculate
{
// function multiply() is part of namespace Calculate
int multiply(int a, int b);
}
#endif
multiply.cpp
#include "multiply.h"
namespace Calculate
{
// define the function multiply() inside namespace Calculate
int multiply(int a, int b)
{
return a * b;
}
}
main.cpp
#include "multiply.h" // for Calculate::multiply()
#include <iostream>
int main()
{
std::cout << Calculate::multiply(6, 7) << '\n';
return 0;
}
If the forward declaration for multiply() wasn't placed inside namespace Calculate, then multiply() would be declared in the global namespace instead, and the compiler would report that it hasn't seen a declaration for Calculate::multiply(6, 7). If the definition of function multiply() wasn't inside namespace Calculate, the linker would report that it couldn't find a matching definition for Calculate::multiply(6, 7).
Multiple namespace blocks are permitted
C++ allows you to declare namespace blocks in multiple locations (either across different files or in multiple places within the same file). All declarations within these namespace blocks are considered part of the namespace.
shapes.h:
#ifndef SHAPES_H
#define SHAPES_H
namespace Geometry
{
constexpr double circleArea{78.54};
}
#endif
volumes.h:
#ifndef VOLUMES_H
#define VOLUMES_H
namespace Geometry
{
// the constant cubeVolume is also part of namespace Geometry
constexpr double cubeVolume{125.0};
}
#endif
main.cpp:
#include "shapes.h" // for Geometry::circleArea
#include "volumes.h" // for Geometry::cubeVolume
#include <iostream>
int main()
{
std::cout << Geometry::circleArea << '\n';
std::cout << Geometry::cubeVolume << '\n';
return 0;
}
This works as expected:
78.54 125
The standard library extensively uses this feature, with each standard library header file containing its declarations inside a namespace std block within that header. Otherwise, the entire standard library would need to be defined in a single header file.
This capability also means you could theoretically add your own functionality to the std namespace. However, doing so causes undefined behavior most of the time, as the std namespace has a special rule prohibiting extension from user code.
Do not add custom functionality to the std namespace.
Namespaces can be nested inside other namespaces. For example:
#include <iostream>
namespace Store
{
namespace Inventory // Inventory is a namespace inside the Store namespace
{
int getTotalItems(int a, int b)
{
return a + b;
}
}
}
int main()
{
std::cout << Store::Inventory::getTotalItems(15, 27) << '\n';
return 0;
}
Note that because namespace Inventory is inside namespace Store, we access getTotalItems as Store::Inventory::getTotalItems.
Since C++17, nested namespaces can also be declared using this more concise syntax:
#include <iostream>
namespace Store::Inventory // Inventory is a namespace inside the Store namespace (C++17 style)
{
int getTotalItems(int a, int b)
{
return a + b;
}
}
int main()
{
std::cout << Store::Inventory::getTotalItems(15, 27) << '\n';
return 0;
}
This is functionally equivalent to the previous example.
If you need to add declarations to just the Store namespace (not Inventory), you can define a separate Store namespace block:
#include <iostream>
namespace Store::Inventory // Inventory is a namespace inside the Store namespace (C++17 style)
{
int getTotalItems(int a, int b)
{
return a + b;
}
}
namespace Store
{
void displayStoreName() {} // This function is in Store only
}
int main()
{
std::cout << Store::Inventory::getTotalItems(15, 27) << '\n';
return 0;
}
Whether you keep separate Store::Inventory definitions or nest Inventory inside Store is a stylistic choice.
Namespace aliases
Because typing the fully qualified name of a variable or function inside a nested namespace can become tedious, C++ allows you to create namespace aliases, which let you temporarily shorten a long sequence of namespaces into something more manageable:
#include <iostream>
namespace Store::Inventory
{
int getTotalItems(int a, int b)
{
return a + b;
}
}
int main()
{
namespace Current = Store::Inventory; // Current now refers to Store::Inventory
std::cout << Current::getTotalItems(15, 27) << '\n'; // This is really Store::Inventory::getTotalItems()
return 0;
} // The Current alias ends here
One significant advantage of namespace aliases: if you ever need to relocate the functionality within Store::Inventory to a different location, you can simply update the Current alias to reflect the new destination, rather than performing a find/replace on every instance of Store::Inventory.
#include <iostream>
namespace Store::Inventory
{
}
namespace Version2
{
int getTotalItems(int a, int b)
{
return a + b;
}
}
int main()
{
namespace Current = Version2; // Current now refers to Version2
std::cout << Current::getTotalItems(15, 27) << '\n'; // We don't have to change this
return 0;
}
Guidelines for using namespaces
It's important to understand that namespaces in C++ weren't originally designed to implement an information hierarchy -- they were primarily designed as a mechanism for preventing naming collisions. Evidence of this can be seen in how the entire standard library exists under a single top-level namespace std. Newer standard library features that introduce many names have begun using nested namespaces (e.g., std::ranges) to avoid naming collisions within the std namespace itself.
- Small personal projects typically don't require namespaces. However, for larger personal projects that include numerous third-party libraries, namespacing your code can help prevent naming collisions with libraries that lack proper namespacing.
The examples in these tutorials typically won't use namespaces unless we're specifically demonstrating namespace concepts, to keep the examples concise.
- Any code you plan to distribute to others should definitely be namespaced to prevent conflicts when integrated into other projects. Often a single top-level namespace suffices (e.g.,
MyLibrary). As an added benefit, placing library code inside a namespace allows users to explore your library's contents using their editor's auto-complete and suggestion features (e.g., typingMyLibrarytriggers autocomplete to display all names insideMyLibrary). - In multi-team organizations, two-level or three-level namespaces are commonly used to prevent naming conflicts between code created by different teams. These often follow one of these patterns:
- Project or library :: module (e.g.,
MyLibrary::Database) - Company or org :: project or library (e.g.,
TechCorp::MyLibrary) - Company or org :: project or library :: module (e.g.,
TechCorp::MyLibrary::Database)
Module-level namespaces can help separate potentially reusable code from application-specific code. For example, physics and math functions could go into one namespace (e.g., Physics::), while language and localization functions go into another (e.g., Localization::). However, directory structures can also achieve this separation (with app-specific code in the project directory tree and reusable code in a separate shared directory tree).
Generally, you should avoid deeply nested namespaces (more than 3 levels).
Summary
- Naming collisions: Occur when two identical identifiers are introduced into the same scope, causing compiler/linker errors
- Exponential growth: As codebases grow linearly, potential naming collisions grow exponentially; define identifiers in the smallest possible scope
- User-defined namespaces: Created using the
namespacekeyword to group related identifiers and prevent naming collisions - Namespace syntax:
namespace NamespaceIdentifier { ... }; no semicolon required after closing brace - Naming convention: Traditionally lowercase; modern practice increasingly favors capital letters for consistency with program-defined types
- Scope resolution operator (
::): Explicitly specifies which namespace contains an identifier (e.g.,Math::process()) - Global namespace access: Use
::identifierwithout a namespace prefix to access identifiers in the global namespace - Qualified names: Identifiers that include namespace specification (e.g.,
std::cout) - Namespace best practices:
- Distribute code should use namespaces to prevent conflicts when integrated into other projects
- Single top-level namespace often suffices for libraries (e.g.,
MyLibrary) - Multi-team organizations commonly use 2-3 level namespaces (e.g.,
Company::Project::Module) - Avoid deeply nested namespaces (more than 3 levels)
- Namespace definition: Must be defined in global scope or nested inside another namespace
- Multiple namespace levels: Projects can use hierarchical namespaces to organize code by module, library, or company
Namespaces are essential for organizing large codebases and preventing naming collisions. The scope resolution operator provides explicit control over identifier resolution, eliminating ambiguity when multiple identifiers share the same name across different namespaces.
Creating Custom Namespaces - Quiz
Test your understanding of the lesson.
Practice Exercises
Creating Namespaces
Create custom namespaces to organize code and avoid naming conflicts.
Lesson Discussion
Share your thoughts and questions
No comments yet. Be the first to share your thoughts!