c++ primer笔记20: namespace

1. Namespaces

Unlike other scopes, a namespace can be defined in several parts. A namespace is made up of the sum of its separately defined parts; a namespace is cumulative. The separate parts of a namespace can be spread over multiple files. The fact that namespace definitions can be discontiguous means that we can compose a namespace from separate interface and implementation files. Thus, a namespace can be organized in the same way that we manage our own class and function definitions:

  • Namespace members that define classes and declarations for the functions and objects that are part of the class interface can be put into header files. These headers can be included by files that use namespace members.
  • The definitions of namepsace members can be put in separate source files.

Namespaces that define multiple, unrelated types should use separate files to represent each type that the namespace defines.

// ---- Sales_item.h ----
namespace cplusplus_primer {
class Sales_item { /* ... */};
Sales_item operator+(const Sales_item&, const Sales_item&);
}

// ---- Sales_item.cc ----
#include "Sales_item.h"
namespace cplusplus_primer {
// definitions for Sales_item members and overloaded operators
}

It is also possible to define a namespace member outside its namespace definition. This definition should look similar to class member functions defined outside a class. The return type and function name are qualified by the namespace name. Once the fully qualified function name is seen, we are in the scope of the namespace. Thus, references to namespace members in the parameter list and the function body can use unqualified names to reference Sales_item.

cplusplus_primer::Sales_item
cplusplus_primer::operator+(const Sales_item& lhs, const Sales_item& rhs)
{
Sales_item ret(lhs);
// ...
}

Although a namespace member can be defined outside its namespace definition, there are restrictions on where this definition can appear. Only namespaces enclosing the member declaration can contain its definition. For example, operator+ could be defined in either the cplusplus_primer namespace or at global scope. It may not be defined in an unrelated namespace.

2. Unnamed Namespaces

Unnamed namespaces are not like other namespaces; the definition of an unnamed namespace is local to a particular file and never
spans multiple text files. An unnamed namespace may be discontiguous within a given file but does not span files. Each file has its own unnamed namespace. Names defined in an unnamed namespace are used directly; after all, there is no namespace name with which to qualify them. It is not possible to use the scope operator to refer to members of unnamed namespaces.

Names defined in an unnamed namespace are found in the same scope as the scope at which the namespace is defined. If an unnamed namespace is defined at the outermost scope in the file, then names in the unnamed namespace must differ from names defined at global scope:

int i; // global declaration for i
namespace {
int i;
}
// error: ambiguous defined globally and in an unnested, unnamed namespace
i = 10;

The use of file static declarations is deprecated by the C++ standard. File statics should be avoided and unnamed namespaces used instead.

3. 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: using declarations、namespace aliases and using directives.

Header files should not contain using directives or using declarations except inside functions or other scopes. A header that includes a using directive or declaration at its top level scope has the effect of injecting that name into the file that includes the header. Headers should define only the names that are part of its interface, not names used in its own implementation.

A namespace alias can be used to associate a shorter synonym with a namespace name. For example

cplusplus_primer::QueryLib::Query tq;
// we could define and use an alias for cplusplus_primer::QueryLib
namespace Qlib = cplusplus_primer::QueryLib;
Qlib::Query tq;

It can be tempting to write programs with using directives, but doing so reintroduces all the problems inherent in name collisions
when using multiple libraries.

The scope of names introduced by a using directive is more complicated than those for using declarations. A using declaration puts the name directly in the same scope in which the using declaration itself appears. It is as if the using declaration is a local alias for the namespace member.

A using directive does not declare local aliases for the namespace member names. Rather, it has the effect of lifting the namespace members into the nearest scope that contains both the namespace itself and the using directive. One place where using directives are useful is in the implementation files for the namespace itself.

namespace blip {
int bi = 16, bj = 15, bk = 23;
}
int bj = 0; // ok: bj inside blip is hidden inside a namespace
void manip()
{
// using directive - names in blip "added" to global scope
using namespace blip;
// clash between ::bj and blip::bj
// detected only if bj is used

++bi; // sets blip::bi to 17
++bj; // error: ambiguous
// global bj or blip::bj?
++::bj; // ok: sets global bj to 1

++blip::bj; // ok: sets blip::bj to 16
int bk = 97; // local bk hides blip::bk
++bk; // sets local bk to 98
}

It is possible for names in the namespace to conflict with other names defined in the enclosing scope. For example, the blip member bj appears to manip as if it were declared at global scope. However, there is another object named bj in global scope. Such conflicts are permitted; but to use the name, we must explicitly indicate which version is wanted. Therefore, the use of bj within manip is ambiguous: The name refers both to the global variable and to the member of namespace blip.

4. Example

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;
}

What are the effects of the declarations and expressions in this code sample if using declarations for all the members of namespace Exercise are located at the location labeled position 1? At position 2 instead? Now answer the same question but replace the using declarations with a using directive for namespace Exercise.
解答:如果Exercise的所有成员使用using声明放在position 1,则 using Exercise::ivar导致重复定义的错误。manip中的dvar会屏蔽Exercise::dvar。如果Exercise的所有成员使用using声明放在position 2,manip中的dvar会出现重复定义的错误。++ivar访问到的是Exercise::ivar,++::ivar访问的是全部变量ivar。如果Exercise的所有成员使用using指示放在position 1,则 ++ivar;会导致二义性错误。如果Exercise的所有成员使用using指示放在position 2, ++ivar;依然会导致二义性错误。

5. Caution: Avoid Using Directives

using directives, which inject all the names from a namespace, are deceptively simple to use: With only a single statement, all the member names of a namespace are suddenly visible. Although this approach may seem simple, it can introduce its own problems. If an application uses many libraries, and if the names within these libraries are made visible with using directives, then we are back to square one, and the global namespace pollution problem reappears.

Moreover, it is possible that a working program will fail to compile when a new version of the library is introduced. This problem can arise if a new version introduces a name that conflicts with a name that the application is using.

Another problem is that ambiguity errors caused by using directives are detected only at the point of use. This late detection means that conflicts can arise long after introducing a particular library. If the program begins using a new part of the library, previously undetected collisions may arise.

Rather than relying on a using directive, it is better to use a using declaration for each namespace name used in the program. Doing so reduces the number of names injected into the namespace. Ambiguity errors caused by using declarations are detected at the point of declaration, not use, and so are easier to find and fix.