Namespaces in C++
There are two students with the same name in a classroom. To distinguish the two students, we need to call them by their names and surnames. A situation like this can occur in C++ as well. It might happen that a function we created with the name sqrt(), but this is already present in the cmath library of C++. In this case, the compiler cannot differentiate between the two function names. Hence, it will yield an error. To avoid this confusion, namespaces are used.
Namespaces provide a method to avoid name conflicts in a project. Namespaces in C++ are used as additional information to differentiate two or more variables, functions, or classes having the same names.
Defining a Namespace in C++
Creating a namespace in C++ is similar to creating a class. We use the keyword namespace followed by the name of the namespace to define a namespace in C++.
In the scope of the namespace, we can declare variables, functions, user-defined data types (like classes), and even nested namespaces.
It is optional to declare all the namespace members all at once. We can define a namespace in multiple parts, and then we can use the scope of each part to define the different identifiers. A namespace is the sum of each separately defined part. A namespace defined in multiple parts is called a discontiguous namespace. We can do so because a namespace in C++ can be characterized in different sections. We will discuss the discontiguous namespaces later in this article.
Using a Namespace in C++
There are three ways to use a namespace in C++:
- Using Scope Resolution Operator
- Using Directive
- Using Declaration
1. Using Scope Resolution Operator (::)
The scope resolution operator ( :: ) can be used with the name of the namespace to call/access any member (variable, function, or class) declared inside a namespace.
For example:
Output:
In the above example, we defined two namespaces - ns1 and ns2. These namespaces had the same function, greet(), inside them. In the main program, we called both these functions using each namespace's name and the scope resolution operator. Hence, we could avoid any conflict that could have resulted in an error.
2. Using Directive
With the help of using namespace directive, we can import an entire namespace from another program file into our program file. The imported namespace will have a global scope. We can also use this directive to import a namespace into another namespace or even another program.
Here is the syntax for the using directive.
For example: Say we have a header file named "namespace\_tutorial1.h" that contains the namespace given below:
Let us include the above file in a C++ program named main.cpp:
Even though the class Students was created in the namespace\_tutorial.h file, we could use this class in the main.cpp file because of the using directive. The topic of Classes in namespaces is covered below in this article.
NOTE: It is generally recommended to avoid using directives because we have no idea what we have imported. It may be a variable, function, or class. We even import those members that we do not need. Using the scope resolution operator or the using declaration in our programs is better.
3. Using Declaration
The using declaration differs slightly from the using directive. In the using declaration, we only import one namespace member at a time with the help of the scope resolution operator. The member imported is only available in the current scope.
Here is the syntax for the using declaration.
NOTE: A namespace member (having a particular name) imported with using declaration can override the same member imported with using directive.
For example:
Consider a file named "namespace_tutorial.h" that contains the following code:
We will now import this file to our main.cpp file:
Output:
In the above program, we used the using directive to import all the members in the namespace ns1. In contrast, we used the using declaration only to import the print_text function from the namespace ns2. When we called the print_text() function in line 14, the print_text() function present inside the namespace ns2 was executed. However, when we called the hello() function in line 17, the hello() function present in the namespace ns1 was executed because we did not import the hello() function from namespace ns2.
Discontiguous Namespaces in C++
We can define namespaces in various program parts that can even be spread over multiple files. This is known as a Discontiguous namespace. The entire namespace is considered as the sum of its separately defined parts.
Let us take an example to understand discontiguous namespaces:
Output:
From the above example, we can observe that the variable var was defined in the first part of the namespace ns, while the function func() was defined in the second part of the same namespace. Ultimately, the compiler merged these two parts to create a single namespace. That's why we could refer to the variable var from the function func().
C++ Nested Namespaces
Namespaces in C++ can be nested. It means that we can create namespaces inside the scope of another namespace.
Let us look at the syntax of nested namespaces.
We can access the members of the nested namespace by outer_ns::inner_ns::inner_ns_member_name.
A nested namespace can access all the members of the outer namespace directly, whereas the outer (enclosing) namespace can only access its nested namespace's members using fully qualified names (nested_ns_name::member_name). However, if the nested namespace is declared inline (inline namespaces are covered below in this article), the outer namespace can directly access its members.
Let us now take an example of nested namespaces:
Output:
In the above example, we created a nested namespace called inner_ns. This namespace had a function called printer(). We used this function in the outer namespace and the main() function using the scope resolution operator and the name(s) of the namespace(s).
Classes in Namespace
We can create classes inside namespaces. Let us take an example to understand how we can do it:
Output:
In the above example, we created a class named Printer inside the namespace ns. In the main function, we created an object of the class Printer using the name of the namespace and the scope resolution operator. Then we used p.print_text() to execute the print_text() function.
We can even declare classes inside a namespace but define them outside it.
Let us take an example to understand this:
Output:
The above example shows how a class is declared inside the namespace block and defined outside it.
Inline Namespaces
An inline namespace is a namespace in C++ that uses the keyword inline in its namespace definition.
Syntax:
If an inline namespace is created inside another namespace, the members of the inline namespace can be treated as if they belong to the enclosing namespace. Say we have a namespace A that contains an inline namespace B, and B contains another inline namespace C. Then, the members of C can be treated as if they were members of both namespaces A and B.
If an inline namespace is not created inside another, then the members of this namespace can be treated as global members.
Let us take an example to understand inline namespaces.
Output:
In the above example, we created two inline namespaces, inline_ns1 and inline_ns2. Because inline_ns1 was not created inside another namespace, the variable var1 declared in inline_ns1 is globally accessible. On the other hand, the namespace inline_ns2 was created inside the namespace ns. So, the variable var2 can be accessed by namespaces inline_ns2 and ns.
The Global Namespace
When we declare a global variable (say int var), we can say that var is in the global namespace. The terms global namespace and global scope are often used interchangeably as they are more or less similar. A namespace defines a scope.
The members of a global namespace can be accessed using the scope resolution operator ( :: ). We do not need to mention a namespace name to access these members.
Let us take an example to understand the global namespace:
Output:
In the above example, we created a variable var in three scopes. We were able to access values of global var using ::var, the value of var present in the namespace ns using ns::var, and the value of var present in the function using the variable name var itself.
Syntax to Declare Class Inside Namespace and Define Outside Namespace
Defining Methods Outside the Namespace
As we have seen, we can declare a class inside the namespace block and define it outside of the block. Similarly, we can declare class methods or functions inside and define them outside the namespace block.
Let us take an example:
Output:
In the above example, we declared two methods, func1() and func2(), inside the namespace ns. Then, we defined each of them outside the scope of ns. Finally, we created an object inside the main function to execute each method.
Namespace Alias
Sometimes the namespace names can be too long. Hence it becomes hectic to write these long names again and again to access the namespace members, especially when we are using nested namespaces. To avoid this problem, we can use namespace aliases. Aliases are alternate names that we can use instead of the original namespace names. Aliases can be created for both outer (enclosing) and inner namespaces.
Here is the syntax for the namespace alias:
Let us take an example of aliases:
Output:
In the above example, we created an alias for outer and inner namespaces. So, instead of writing outer_ns::inner_ns::func2(), we could write i_ns::func2() to access the function func2(). And, instead of writing outer_ns::func1(), we could write o_ns::func1() to access the function func1().
Namespace std
In most of our C++ programs, we usually write using namespace std. This namespace std is in the iostream header file. The std stands for standard. The iostream header file contains a lot of commonly used members like cout, cin, endl, etc. So to avoid writing std::cout or std::endl again and again, we usually add the using directive - using namespace std in our programs.
Instead of employing the using directive (using namespace std), we can even use the using declaration (using std::cout) only to import the commonly used iostream file members. This can help us avoid naming conflicts as we may create functions or classes in our program with names already existing in the std namespace.
Anonymous or Unnamed Namespaces
C++ supports the use of anonymous or unnamed namespaces. Anonymous namespaces are those namespaces that don't have a name. If a namespace is anonymous, the members defined in this namespace can be accessed directly, just like we access global variables. However, the members of this namespace can only be accessed within the file in which the namespace was created.
Here is the syntax for an anonymous or unnamed namespace:
Let us take an example of anonymous namespaces:
Output:
From the above example, we can observe that we were able to access the function square() that was created inside the unnamed namespace directly in the main() function.
Advantages of Namespace
- Ease in using libraries - Namespaces help us use different libraries together in one program and differentiate between variable names using the scope resolution operator.
- Reusable variable names - We can use the same variable names multiple times in one C++ program.
- Enhance code readability - We can specify similar code in different files and libraries using a namespace to enhance the code readability.
Disadvantages of Namespace
- Ambiguous function calls - If functions are defined in multiple places, they could be called ambiguously because of the namespace.
- Problems with the using directive - If we use the using directive in our programs, it may lead to unexpected program behavior.
Conclusion
- Namespaces allow us to group entities like variables, classes, objects, and functions under a single name.
- Namespaces can have names or can be unnamed.
- A namespace can be defined in multiple parts and multiple files. The final namespace is the addition of all the individual parts.
- Namespaces in C++ can be used in three ways - the scope resolution operator, the using directive, or the using declaration.
- If a nested namespace is declared inline, the members of the inline namespace can be treated as if they belong to the outer or enclosing namespace.
- Namespaces can have alias names, i.e., we can call them using alternative names.