18.2.2. Using Namespace Members

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.

Namespace Aliases

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;


Image Note

A namespace can have many synonyms, or aliases. All the aliases and the original namespace name can be used interchangeably.


using Declarations: A Recap

A 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 Directives

A 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.


Image Warning

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 Scope

The 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 Example

Let’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.

Headers and using Declarations or Directives

A 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).


Exercises Section 18.2.2

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.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.145.17.140