In one form or another, we have used namespaces throughout the textbook in all the example programs and the programming assignments you have completed. Chapter 1 introduced the standard namespace, std, and illustrated two options for accessing the I/O system with it. This brief section will demonstrate how to create and, more generally, use namespaces.
"A namespace is a declarative region that provides a scope to the identifiers (the names of types, functions, variables, etc) inside it. Namespaces are used to organize code into logical groups and to prevent name collisions that can occur especially when your code base includes multiple libraries" (Microsoft Documentation). To understand how namespaces work and why we need them, we must understand what a "name collision" means.
Linguists estimate that there are more than a million words in English, and there are likely similar numbers in other languages. So, it seems like we should be able to write a program without running out of identifier names. For example, using English's 26-character alphabet, we can form nearly 457,000 four-character patterns, almost half the number of words in the language. But most of those patterns are meaningless. Furthermore, programmers often name programming elements based on their role in a program (i.e., on what they do). This practice sets the stage for potential name collisions.
It's easy to resolve name collisions by changing one of the colliding names when a programming team works together to create a system. But when we create a system incorporating multi-sourced components delivered as executables and object-code libraries (Figure 4, step 3), it's not possible to change conflicting names. I encountered this problem while integrating a database management system with library code licensed by different vendors. Namespaces solves this problem by creating named scopes.
Vendors avoid name collisions by declaring functions and variables in uniquely named namespaces with names typically based on corporate names, which are trademarked and, therefore, unique. Namespaces included in the C++ API are managed and reserved by the ANSI C++ committee. C++ binds the namespace name to the names of its functions and variables with the scope resolution operator. The name "scope resolution operator" suggests that it helps the compiler determine the scope of otherwise ambiguous programming elements.
namespace Acme { void print(string name); }; namespace Widgets_galore { void print(string part); }; |
void Acme::print(string name) { cout << "Acme " << name << endl; }; void Widgets_galore::print(string part) { cout << "Widgets_galore " << part << endl; } |
(a) | (b) |
int main() { Acme::print("Dilbert"); Widgets_galore::print("wing nut"); return 0; } |
// Error - a namespace is an invalid data type Acme m; Widgets_galore* wg = new Widgets_galore; |
(c) | (d) |
class beta
{
public:
string to_string(int value)
{ return std::to_string(value); }
}; |
namespace std { string to_string(int value); }; |
(a) | (b) |
string
class to_string function (see string
To Number and Number To string
). Without the namespace name and the scope resolution operator, the call becomes a direct recursion function call.
Programmers use the scope resolution operator to resolve the ambiguous use of function names as illustrated here by to_string.