As our programs grow larger and more complex, and we build more and more libraries and modules, it becomes increasingly likely that we want to use the same name for different classes, structs, or functions in different parts of our code. For example, if we were writing a program that used both a graphics library and a math library, both might want to define a Point class. Both versions of βPointβ might include members to represent x and y values. But the graphics version of βPointβ might care about what color it was while the math version would not. And the two versions would need different behaviors.
Namespaces are a way to group related names together to avoid name collisions. When we do not explicitly put something in a namespace, it goes into the global namespace. If there are two things with the same name in the global namespace, like two versions of Point, the compiler will not know which one we mean, and we will get an error. To avoid this, we need to put each version of Point in its own namespace, such as graphics::Point and math::Point.
We are already somewhat familiar with namespaces from using the std namespace that contains all the standard C++ library features. We know that the full name of something like string is actually std::string, where std is the namespace and string is the name within that namespace. We also know that we either need to use the full name or bring names into the current scope with a using declaration.
The original C++ language did not include a standard library or string class. It was quite common for programmers or organizations to create their own βstringβ classes before the standard library was widely adopted.
To define a namespace, we use the namespace keyword followed by the name of the namespace and a block containing the definitions that belong to that namespace. These definitions can include classes, structs, functions, and variables. For example:
This defines a namespace called graphics that contains a struct called Point. To use this Point struct, we would refer to it as graphics::Point. That way, we know exactly which Point we are referring to, even if there are other Point definitions in other namespaces. (We will use structs since they are easier to write small versions of than classes. But the same ideas applyto classes.)
If we want, we can bring names from a namespace into the current scope with a using declaration. For example, we could write using graphics::Point; to bring the Point struct from the graphics namespace into the current scope, so we can just write Point instead of graphics::Point. We can still access other points by their full names, like math::Point.
We do have to be careful when using using directives because if two namespaces have names that conflict, bringing them both into the current scope can cause ambiguity errors. Try adding using namespace math; below the existing using namespace graphics; directive and see what happens.
test.cpp: In function βint main()β:
test.cpp:25:3: error: reference to βPointβ is ambiguous
25 | Point gPoint;
| ^~~~~
test.cpp:13:10: note: candidates are: βstruct math::Pointβ
13 | struct Point {
| ^~~~~
test.cpp:6:10: note: βstruct graphics::Pointβ
6 | struct Point {
| ^~~~~
The compiler is telling us that it does not know which Point we are referring to because both the graphics and math namespaces have a Point struct. To fix this, we can either remove one of the using directives or refer to the Point structs by their full names.
We can also just bring in specific names from a namespace using a using declaration. For example, imagine graphics has multiple members. We could write using graphics::Point; to bring only the Point struct from the graphics namespace into the current scope.
In this sample, we add a struct Circle to the graphics namespace. We then bring in only the Point struct from the graphics namespace into the current scope:
Since we did not bring in all names from the graphics namespace, we still need to qualify the call to Circle with graphics::. If you try to remove that qualification, you will get an error saying that Circle is not declared in the current scope.