Referring to namespace members as namespace_name::member_name
is admittedly cumbersome, especially if the namespace name is long. Fortunately, there are ways to make it easier to use namespace members. Our programs have used one of these ways, using
declarations (§ 3.1, p. 82). The others, namespace aliases and using
directives, will be described in this section.
A namespace alias can be used to associate a shorter synonym with a namespace name. For example, a long namespace name such as
namespace cplusplus_primer { /* ... */ };
can be associated with a shorter synonym as follows:
namespace primer = cplusplus_primer;
A namespace alias declaration begins with the keyword namespace
, followed by the alias name, followed by the =
sign, followed by the original namespace name and a semicolon. It is an error if the original namespace name has not already been defined as a namespace.
A namespace alias can also refer to a nested namespace:
namespace Qlib = cplusplus_primer::QueryLib;
Qlib::Query q;
A namespace can have many synonyms, or aliases. All the aliases and the original namespace name can be used interchangeably.
using
Declarations: A RecapA using
declaration introduces only one namespace member at a time. It allows us to be very specific regarding which names are used in our programs.
Names introduced in a using
declaration obey normal scope rules: They are visible from the point of the using
declaration to the end of the scope in which the declaration appears. Entities with the same name defined in an outer scope are hidden. The unqualified name may be used only within the scope in which it is declared and in scopes nested within that scope. Once the scope ends, the fully qualified name must be used.
A using
declaration can appear in global, local, namespace, or class scope. In class scope, such declarations may only refer to a base class member (§ 15.5, p. 615).
using
DirectivesA using
directive, like a using
declaration, allows us to use the unqualified form of a namespace name. Unlike a using
declaration, we retain no control over which names are made visible—they all are.
A using
directive begins with the keyword using
, followed by the keyword namespace
, followed by a namespace name. It is an error if the name is not a previously defined namespace name. A using
directive may appear in global, local, or namespace scope. It may not appear in a class scope.
These directives make all the names from a specific namespace visible without qualification. The short form names can be used from the point of the using
directive to the end of the scope in which the using
directive appears.
Providing a using
directive for namespaces, such as std
, that our application does not control reintroduces all the name collision problems inherent in using multiple libraries.
using
Directives and ScopeThe scope of names introduced by a using
directive is more complicated than the scope of names in using
declarations. As we’ve seen, a using
declaration puts the name in the same scope as that of the using
declaration itself. It is as if the using
declaration declares a local alias for the namespace member.
A using
directive does not declare local aliases. Rather, it has the effect of lifting the namespace members into the nearest scope that contains both the namespace itself and the using
directive.
This difference in scope between a using
declaration and a using
directive stems directly from how these two facilities work. In the case of a using
declaration, we are simply making name directly accessible in the local scope. In contrast, a using
directive makes the entire contents of a namespace available In general, a namespace might include definitions that cannot appear in a local scope. As a consequence, a using
directive is treated as if it appeared in the nearest enclosing namespace scope.
In the simplest case, assume we have a namespace A
and a function f
, both defined at global scope. If f
has a using
directive for A
, then in f
it will be as if the names in A
appeared in the global scope prior to the definition of f
:
// namespace A and function f are defined at global scope
namespace A {
int i, j;
}
void f()
{
using namespace A; // injects the names from A into the global scope
cout << i * j << endl; // uses i and j from namespace A
// ...
}
using
Directives ExampleLet’s look at an example:
namespace blip {
int i = 16, j = 15, k = 23;
// other declarations
}
int j = 0; // ok: j inside blip is hidden inside a namespace
void manip()
{
// using directive; the names in blip are ''added'' to the global scope
using namespace blip; // clash between ::j and blip::j
// detected only if j is used
++i; // sets blip::i to 17
++j; // error ambiguous: global j or blip::j?
++::j; // ok: sets global j to 1
++blip::j; // ok: sets blip::j to 16
int k = 97; // local k hides blip::k
++k; // sets local k to 98
}
The using
directive in manip
makes all the names in blip
directly accessible; code inside manip
can refer to the names of these members, using their short form.
The members of blip
appear as if they were defined in the scope in which both blip
and manip
are defined. Assuming manip
is defined at global scope, then the members of blip
appear as if they were declared in global scope.
When a namespace is injected into an enclosing scope, it is possible for names in the namespace to conflict with other names defined in that (enclosing) scope. For example, inside manip
, the blip
member j
conflicts with the global object named j
. Such conflicts are permitted, but to use the name, we must explicitly indicate which version is wanted. Any unqualified use of j
within manip
is ambiguous.
To use a name such as j
, we must use the scope operator to indicate which name is wanted. We would write ::j
to obtain the variable defined in global scope. To use the j
defined in blip
, we must use its qualified name, blip::j
.
Because the names are in different scopes, local declarations within manip
may hide some of the namespace member names. The local variable k
hides the namespace member blip::k
. Referring to k
within manip
is not ambiguous; it refers to the local variable k
.
using
Declarations or DirectivesA header that has a using
directive or declaration at its top-level scope injects names into every file that includes the header. Ordinarily, headers should define only the names that are part of its interface, not names used in its own implementation. As a result, header files should not contain using
directives or using
declarations except inside functions or namespaces (§ 3.1, p. 83).
Exercise 18.15: Explain the differences between using
declarations and directives.
Exercise 18.16: Explain the following code assuming using
declarations for all the members of namespace Exercise
are located at the location labeled position 1. What if they appear at position 2 instead? Now answer the same question but replace the using
declarations with a using
directive for namespace Exercise
.
namespace Exercise {
int ivar = 0;
double dvar = 0;
const int limit = 1000;
}
int ivar = 0;
// position 1
void manip() {
// position 2
double dvar = 3.1416;
int iobj = limit + 1;
++ivar;
++::ivar;
}
Exercise 18.17: Write code to test your answers to the previous question.