17. Namespaces

Always design a thing by considering it in its next larger context.

Eliel Saarinen

Global scope problems — ideals for a solution — namespaces, using-declarations, and using-directives — how to use namespaces — name-spaces and classes — C compatibility.

17.1 Introduction

C provides a single global namespace for all names that don’t conveniently fit into a single function, a single struct, or a single translation unit. This causes problems with name clashes. I first grappled with this problem in the original design of C++ by defaulting all names to be local to a translation unit and requiring an explicit extern declaration to make them visible to other translation units. As described in §3.12, this idea was neither sufficient to solve the problem nor sufficiently compatible to be acceptable, so it failed.

When I devised the type-safe linkage mechanism (§11.3), I reconsidered the problem. I observed that a slight change to the

extern "C" { /* ... */ }

syntax, semantics, and implementation technique would allow us to have

extern XXX { /* ... */ }

mean that names declared in XXX were in a separate scope XXX and accessible from other scopes only when qualified by XXX:: in exactly the same way static class members are accessed from outside their class.

For various reasons, mostly related to lack of time, this idea lay dormant until it resurfaced in the ANSI/ISO committee discussions early in 1991. First, Keith Rowe from Microsoft presented a proposal that suggested the notation

bundle XXX { /* ... */ } ;

as a mechanism for defining a named scope and an operator use for bringing all names from a bundle into another scope. This led to a – not very vigorous – discussion among a few members of the extensions group including Steve Dovich, Dag Brück, Martin O’Riordan, and me. Eventually, Volker Bauche, Roland Hartinger, and Erwin Unruh from Siemens refined the ideas discussed into a proposal that didn’t use new keywords:

:: XXX :: { / * ... * / } ;

This led to a serious discussion in the extensions group. In particular, Martin O’Riordan demonstrated that this :: notation led to ambiguities with :: used for class members and for global names.

By early 1993,1 had – with the help of multi-megabyte email exchanges and discussions at the standards meetings – synthesized a coherent proposal. I recall technical contributions on namespaces from Dag Brück, John Bruns, Steve Dovich, Bill Gibbons, Philippe Gautron, Tony Hansen, Peter Juhl, Andrew Koenig, Eric Krohn, Doug McIlroy, Richard Minner, Martin O’Riordan, John Skaller, Jerry Schwarz, Mark Terribile, and Mike Vilot. In addition, Mike Vilot argued for immediate development of the ideas into a definite proposal so that the facilities would be available for addressing the inevitable naming problems in the ISO C++ library. Namespaces were voted into C++ at the Munich meeting in July 1993. At the San Jose meeting in November 1993, it was decided to use namespaces to control names in the standard C and C++ libraries.

17.2 The Problem

Having only a single global scope makes it unnecessarily difficult to write program fragments that can be linked together without fear of name clashes. For example:

// my.h:
    char f(char);
    int f(int);
    class String { /* ... */ };

// your.h:
    char f(char);
    double f(double);
    class String { /* ... */ };

Given these definitions, a third party cannot easily use both my. h and your. h.

Note that some of these names will appear in object code, and that some programs will be shipped without source. This implies that macro-like schemes that change the appearance of programs without actually changing the names presented to a linker are insufficient.

17.2.1 Workarounds

There are several workarounds. For example:

// my.h:
    char my_f(char);
    int my_f(int);
    class my_String { /* ... */ };


// your.h:
    char yo_f(char);
    double yo_f(double);
    class yo_String { /* ... */ };

This approach is not uncommon, but it is quite ugly and – unless the prefix strings are short – unpleasant for the user. Another problem is that there are only a few hundred two-letter prefixes and already hundreds of C++ libraries. This is one of the oldest problems in the book. Old-time C programmers will be reminded of the time when struct member names were given one or two letter suffixes to avoid clashes with members of other structs.

Macro hackery can make this approach even nastier (or even nicer, if you happen to like macros):

// my.h:
    #define my(X) myprefix_##X

    char my(f)(char);
    int my(f)(int);
    class my(String) { /* ... */ };


// your.h:
    #define yo(X) your_##X

    char yo(f)(char);
    double yo(f)(double);
    class yo(String) { /* ... */ };

The idea is to allow longer prefixes in the name used for linkage while leaving the names used in the program short. As with all macro schemes, this creates a problem for tools: Either the tool keeps track of the mapping (complicating the tool) or the user will have to do so (complicating programming and maintenance).

An alternative approach – often preferred by people who dislike macros – is to wrap related information into a class:

// my.h:
  class My {
  public:
      static char f(char);
      static int f(int);
      class String { /* ... */ };
  };

// your.h:
    class Your {
    public:
        static char f(char);
        static double f(double);
        class String { /* ... */ };
    };

Unfortunately, this approach suffers from many little inconveniences. Not all global declarations can be simply transferred into a class and some change their meaning if you do so. For example, global functions and variables must be specified as static members to avoid semantic changes and the function bodies, and initializers must in general be separated from their declarations.

17.3 Ideals for a Solution

Many mechanisms can be used to provide solutions to namespace problems. Indeed, most languages can claim to have at least the rudiments of one. For example, C has its static functions, Pascal its nested scopes, C++ its classes, but we need to go to languages such as PL/I, Ada, Modula-2, Modula-3 [Nelson, 1991], ML [Wikström,1987], and CLOS [Kiczales,1992] for more complete solutions.

So what would a good namespace mechanism give us in C++? A lengthy and voluminous discussion on the ANSI/ISO committee’s extensions working group mailing list provided a list:

[1] The ability to link two libraries without name clashes.

[2] The ability to introduce names without fear of clashing with someone else’s names (for example, names used in a library I haven’t heard of, or names I haven’t heard of in a library I thought I knew).

[3] The ability to add a name to the implementation of a library without affecting its users.

[4] The ability to select names from two different libraries even if those two libraries use the same names.

[5] The ability to resolve name clashes without modifying functions (that is, through declarations manipulating the namespace resolution).

[6] The ability to add a name to a namespace without fear of causing a quiet change to code using other namespaces (we cannot provide such a guarantee for code using the namespace being added to).

[7] The ability to avoid clashes among namespace names (in particular, the ability to have the “real” or linkage name longer than the name used in user code).

[8] The ability to use the namespace mechanism to deal with the standard libraries.

[9] C and C++ compatibility.

[10] No added cost in link time or run time for the users of namespaces.

[11] No added verbosity for the users of namespaces compared to users of global names.

[12] The ability to indicate explicitly where a name is supposed to come from in code using the name.

In addition, a good solution must be simple. I defined simple as:

[1] A mechanism that can be explained to the degree needed for serious use in less than ten minutes. Explaining any mechanism to the satisfaction of language lawyers takes much longer.

[2] Something a C++ compiler writer can implement in less than two weeks.

Naturally, simplicity in this sense cannot be proven rigorously. For example, the time needed to understand something will vary greatly between people with different backgrounds and different levels of abilities.

There were also some properties that were asked for that we deliberately excluded from the criteria for the namespace mechanism:

[1] The ability to take binaries with clashing link names and link them together. This can be done by tools in every system, but I don’t see a language mechanism that could easily be implemented without significant effort or overhead on all systems. There are too many linkers and too many object code formats around to make it feasible to change them. For a solution to be useful for C++ it must require only facilities provided by almost all current linkers.

[2] The ability to provide arbitrary synonyms for names used in libraries. Existing mechanisms, such as typedef, references, and macros provide mechanisms for providing synonyms only in specific cases, and I distrust general renaming facilities; see §12.8.

This implies that disambiguation must be compiled into the object code by providers of program fragments. In particular, library providers will have to use a technique that allows users to disambiguate. Fortunately, the library providers will be some of the main beneficiaries from a systematic use of namespaces because they (partly through their users) are the main sufferers in the current situation.

Naturally, it is possible to add criteria to these lists, and no two people will agree to the exact importance of the criteria. Nevertheless, these lists give an idea of the complexity of the problem and the demands that a solution must meet.

After first presenting these criteria, I had the opportunity to test the namespace design for simplicity according to these criteria. Peter Juhl completed a pilot implementation in five days, and I explained the basics of namespaces to several people in less than ten minutes using just a couple of foils. Their follow-up questions showed understanding and the ability to deduce some of the uses of namespaces that I hadn’t explained. That satisfied me that the namespace facilities were simple enough. Further implementation experience, discussion of the namespace concept, and some use have increased my confidence in that conclusion.

17.4 The Solution: Namespaces

The adopted solution is fundamentally simple. It provides four new mechanisms:

[1] A mechanism for defining a scope that holds what have traditionally been global declarations in C and C++: a namespace. Such scopes can be named and a namespace’s members can be referred to using the traditional notation for class members: namespace_name::member_name. In fact, a class scope can be seen as a special case of a namespace scope.

[2] A mechanism for defining a local synonym for a namespace name.

[3] A mechanism allowing a member of a namespace to be accessed without the explicit namespace_name:: qualification: a using-declaration.

[4] A mechanism allowing all members of a namespace to be accessed without the explicit name space_name:: qualification: a using-directive.

This meets the criteria from §17.3. In addition, it solves a long-standing problem with access to base class members from a derived class scope (see §17.5.1, §17.5.2) and renders static redundant as used for global names (see §17.5.3).

Consider:

namespace A {
    void f(int); void f(char);
    class String { /* ... */ };
    // . . .
}

The names declared within the namespace braces are in namespace A and do not collide with global names or names in any other namespace. Namespace declarations (including definitions) have exactly the same semantics as global declarations, except that the scope of their names is restricted to the namespace.

Programmers can use such names by explicitly qualifying uses:

A::String s1 = "Annemarie";

void gl()
{
    A::f (1);
}

Alternatively, we can explicitly make an individual name from a specific library available for use without qualification by a using-declaration:

using A::String;
String s2 = "Nicholas";   // meaning A::String

void g2()
{
    using A::f;   // introduce local synonym for A’s f
    f(2);         // meaning A::f
}

A using-declaration introduces a synonym for the name it mentions into the local scope.

Alternatively, we can explicitly make all names from a specific library available for use without qualification by a using-directive:

using namespace A;      // make all names from A accessible
String s3 = "Marian";   // meaning A::String

void g3()
{
    f(3);         // meaning A::f
}

A using-directive doesn’t introduce names into the local scope; it simply makes the names from the namespace accessible.

My original design used a simpler and less verbose syntax for using-directives:

using A; // meaning ''using namespace A;''

This created total confusion between using-directives and using-declarations. Most of this confusion disappeared when I introduced the more explicit syntax. The more explicit form also simplified the parser.

I anticipated a need to avoid repetition of long namespace names. Therefore, the original design allowed for several member names to be mentioned in a single using-declaration:

using X:: (f,g,h) ;

This is syntactically ugly, and so were all the alternatives we considered. More precisely: every alternative we considered was considered unbearably ugly by several people. Having used namespaces a bit, I found far less need for such lists than I had expected. I also tended to overlook such lists when reading code because they resemble function declarations too much, so I fell into the habit of using repeated using-declarations instead:

using X::f;
using X::g;
using X::h;

Consequently, there is no special form of a using-declaration that specifies a list of member names.

Namespaces provide an example of a feature that became noticeably simpler through experimentation. Namespaces are also easy to implement because they fit exactly with C++’s view of scope and class.

17.4.1 Views on Namespace Use

The three ways of accessing names from a namespace are the result of long discussions trying to address apparently irreconcilable views on what is important for naming in a large program. Some people insist that reliable and maintainable programs can be obtained only if every use of a non-local name is explicitly qualified. Naturally, these people insist on the use of explicit qualification and express serious doubts about the value of using-declarations and even more about using-directives.

Other people denounce explicit qualification as unacceptably verbose, making code too hard to change, limiting flexibility, and making a transition to the use of namespaces infeasible. Naturally, these people argue for using-directives and other mechanisms for mapping ordinary short names into namespaces.

I am sympathetic to the less radical variants of both views. Consequently, name-spaces allow each style to be used and enforce neither. Local style guidelines can – as usual – be used to enforce restrictions that would be unwise to impose on all users through a language rule.

Many people – quite reasonably – worry about ordinary unqualified names being “hijacked;” that is, being bound to an object or a function different from the one intended by the programmer. Every C programmer has suffered from this phenomenon at some time or other. Explicit qualification greatly alleviates such problems. A similar, yet distinct, worry is that it can be hard to find the declaration of a name and hard to guess the meaning of an expression containing it. Explicit qualification gives such a strong clue that it often isn’t necessary to look for the declaration: The name of a library plus the name of a function often makes the meaning of an expression obvious. For these reasons, explicit qualification should be preferred for unusual or infrequently used non-local names. The increase of code clarity can be significant.

On the other hand, explicit qualification of names that everybody knows (or at least ought to know) and of frequently used names can become a real nuisance. For example, writing stdio::rprintf, math::sqrt, and iostream::cout is not going to help anyone acquainted with C++. The added visual clutter easily becomes a source of errors. This argues strongly for a mechanism like using-declarations or using-directives. Of these, a using-declaration is the more discriminating and by far the less dangerous. A using-directive:

using namespace X;

makes an unknown set of names available. In particular, this directive may make one set of names available today, but if changes are made to X, a different set of names may be made available tomorrow. People who find this worrying will prefer to list the names they want to use from X explicitly in using-declarations:

using X::f;
using X::g;
using X::h;

However, the ability to gain access to every name from a namespace without having to name them and to have that set of available names change with the definition of X without having to modify user code is occasionally exactly what is desired.

17.4.2 Getting Namespaces into Use

Given the millions of lines of C++ code relying on global names and existing libraries, I considered the most important question about namespaces to be: How can we get namespaces into use? It doesn’t matter how elegant namespace-based code can be if there is no simple transition path that users and library providers can follow to introduce namespaces. Requiring major rewrites didn’t seem a viable option.

Consider the canonical first C program:

#include <stdio.h>

int main()
{
    printf("Hello, world ");
}

Breaking this program wouldn’t be a good idea. I didn’t consider making standard libraries special cases a good idea either. I considered it important to ensure that the namespace mechanism was good enough to serve the standard libraries. In this way, the standards committee can’t demand privileges for their libraries that they are not willing to extend to purveyors of other libraries. In other words, don’t impose rules on others unless you are willing to live by those rules yourself.

The using-directive is the key to achieving this. For example, stdio.h is wrapped in a namespace like this:

// stdio.h:

   namespace std {
       // . . .
       int printf(const char* ...);
       // . . .
   }
   using namespace std;

This achieves backwards compatibility, and a new header file stdio is defined for people who don’t want the names implicitly available:

// stdio:

   namespace std {
       // . . .
       int printf(const char* ...);
       // . . .
   }

People who worry about replication of declarations will of course define stdio.h by including stdio:

// stdio.h:

    #include <stdio>
    using namespace std;

Personally, I consider using-directives primarily as a transition tool. Most programs can be expressed more clearly using explicit qualification and using-declarations when referring to names from other namespaces.

Naturally, names from an enclosing namespace require no qualification:

namespace A {
    void f();
    void g()
    {
      f(); // call A::f; no qualifier necessary
      // . . .
    }
}

void A::f()
  {
    g(); // call A::g; no qualifier necessary
    // . . .
  }

In this respect, namespaces behave exactly like classes.

17.4.3 Namespace Aliases

If users give their namespaces short names, the names of different namespaces will clash:

namespace A { // short namespace name:
              // will clash (eventually)
    // . . .
};

A::String s1 = "asdf";
A::String s2 = "lkjh";

However, long namespace names can be tedious:

namespace American_Telephone_and_Telegraph { // too long
                                             // to use in
                                             // real code
    // . . .
}

American_Telephone_and_Telegraph::String s3 = "asdf";
American_Telephone_and_Telegraph::String s4 = "lkjh";

This dilemma can be resolved by providing a short alias for a longer namespace name:

// use namespace alias to shorten names:

namespace ATT = American_Telephone_and_Telegraph;

ATT::String s3 = "asdf";
ATT::String s4 = "lkjh";

This feature also allows a user to refer to “The library” without having to say exactly which library is actually used each time. In fact, namespaces can be used to compose interfaces out of names from more than one namespace:

namespace My_interface {
  using namespace American_Telephone_and_Telegraph;
  using My_own::String;
  using namespace 01;
    // resolve clash of definitions of "Flags'
    // from OI and American_Telephone_and_Telegraph:
  typedef int Flags;
    // . . .
}

17.4.4 Using Namespaces to Manage Releases

As an example of namespaces, I’ll show how a library supplier might use namespaces to manage incompatible changes between releases. This technique was first pointed out to me by Tanj Bennett. Here is my releasel:

namespace releasel {
    // . . .
    class X {
        Impl::Xrep* p;
    public:
        virtual void fl() = 0;
        virtual void f2() = 0;
        // . . .
    };
    // . . .
}

Impl is some namespace where I keep my implementation details.

A user will write code like this:

class XX : public releasel::X {
    int xxl;
    // . . .
  public:
    void fl();
    void f2();
    virtual void ffl();
    virtual void ff2();
    // . . .
  };

This implies that I, as a library provider, cannot change the size of release1::X objects (for example, by adding data members), add or rearrange virtual functions, etc., because that would imply that the user’s code would have to be recompiled to readjust the object layout to accommodate my changes. There are implementations of C++ that insulate users from such changes, but they are not common, so as a library provider I cannot rely on them without tying myself to a single compiler supplier. I might encourage users not to derive from my library classes in this way, but they’ll do it anyway and complain about having to recompile even when they have been warned.

I need a better solution. Using namespaces to distinguish different versions, my release2 might look like this:

namespace releasel { // releasel supplied for compatibility
    // . . .
    class X {
        Impl::Xrep* p; // Impl::Xrep has changed
                       // to accommodate release2
    public:
        virtual void fl() = 0;
        virtual void f2() = 0;
        // . . .
    };
    // . . .
}

namespace release2 {
    // . . .
    class X {
        Impl::Xrep* p;
    public:
        virtual void f2() = 0; // new ordering
        virtual void f3() = 0; // more function
        virtual void f1() = 0;
        // . . .
    };
    // . . .
}

Old code uses release1, and new code uses release2. New and old code not only work, but coexist. The headers for release1 and release2 are distinct so that the user need only #include the necessary minimum. To ease upgrades, a user can use a namespace alias to localize the effect of a version change. A single file

// lib.h:
    namespace lib = releasel;
    // . . .

can include all the version-dependent stuff and be used everywhere like this:

#include "lib.h"

class XX : public lib::X {
    // . . .
};

which is upgraded to use a new release by a single change:

// lib.h:
    namespace lib = release2;
    // . .

This update is done only when there is a reason to use release2, time to recompile, and time to deal with possible source code incompatibilities between the releases.

17.4.5 Details

This section presents technical details relating to scope resolution, the global scope, overloading, nested namespaces, and composition of namespaces from separate parts.

17.4.5.1 Convenience vs. Safety

A using-declaration adds to a local scope. A using-directive does not; it simply renders names accessible. For example:

namespace X {
    int i, j, k;
}

int k;

void fl()
{
    int i = 0;
    using namespace X; // make names from X accessible
    i++;               // local i
    j++;               // X:: j
    k++;               // error: X::k or global k ?
    ::k++;             // the global k
    X::k++;            // X's k
}

void f2 ()
{
    int i = 0;
    using X::i;        // error: i declared twice in f2()
    using X:: j;
    using X::k;        // hides global k

    i++;
    j++;               // X::j
    k++;               // X::k
}

This preserves the important property that a locally declared name (declared either by an ordinary declaration or by a using-declaration) hides non-local declarations of the same name, and any illegal overloadings of the name are detected at the point of declaration.

As shown, giving no preference to the global scope over namespaces made accessible in the global scope provides some protection against accidental name clashes.

Non-local names, on the other hand, are found in the context in which they were declared and treated just like other non-local names. In particular, errors relating to a using-directive are detected only at the point of use. This saves the programmer from having a program fail because of potential errors. For example:

namespace A {
    int x;
}

namespace B {
    int x;
}

void f()
{
    using namespace A;
    using namespace B;   // ok: no error here

    A::x++;    // ok
    B::x++;    // ok
    x++;       // error: A::x or B::x ?
}

17.4.5.2 The Global Scope

With the introduction of namespaces, the global scope becomes just another name-space. The global namespace is odd only in that you don’t have to mention its name in an explicit qualification: ::f means “the f declared in the global namespace,” whereas X::f means “the f declared in namespace X.” Consider:

int a;

void f()
{
     int a = 0;
     a++;     // local a
     ::a++;   // global a
}

If we wrap a namespace around this and add yet another variable called a, we get:

int a;

namespace X {
    int a;

    void f()
    {
         int a = 0;
         a++;        // local a
         X::a++;     // X::a
         ::a++;      // X::a or global a ? -- the global a !
    }
}

In other words, qualification by unary :: means “global” rather than “in the nearest enclosing namespace.” The latter would ensure that wrapping arbitrary code in a namespace implied no change of meaning. However, then the global scope would not have a name, and that would be in variance with the view that the global namespace is just an ordinary namespace with an odd name. Consequently, we chose the former meaning so that :: a refers to the a declared in the global scope.

I expect to see a radical decrease in the use of global names. The rules for name-spaces were specifically crafted to give no advantages to a “lazy” user of global names over someone who takes care not to pollute the global scope.

Note that a using-directive does not declare names in the scope in which it occurs:

namespace X {
    int a;
    int b;
    // . . .
}

using namespace X; // make all names from X accessible
using X::b;        // declare local synonym for X::b

int i1 = ::a; // error: no ''a'' declared in global scope
int i2 = ::b; // ok: find the local synonym for X::b

This implies that old code using explicit :: to access global library functions will break when the library is put into a namespace. The solution is either to modify the code to explicitly mention the new library namespace name or to introduce suitable global using-declarations.

17.4.5.3 Overloading

The most controversial aspect of the namespace proposal was the decision to allow overloading across namespaces according to the usual overloading rules. Consider:

namespace A {
    void f(int);
    // . . .
}
using namespace A;

namespace B {
    void f(char);
    // . . .
}
using namespace B;

void g()
{
    f('a'); // calls B::f(char)
}

A user who hasn’t looked carefully at namespace B might expect A::f(int) to be called. Worse, a user who looked carefully at the program last year and didn’t notice that a declaration of f(char) was added to B in a later release might get surprised.

However, this problem occurs only when you maintain programs that explicitly use using namespace twice for the same scope – a non-recommended practice for newly written software. A call of a function that has two legal resolutions from different namespaces is also an obvious candidate for an optional compiler warning even if the ordinary overload resolution rules prefer the one resolution over the other. I see using-directives as primarily a transition aid and writers of new code can avoid many theoretical and a few real problems by sticking to explicit qualification and using-declarations wherever possible.

My reason for allowing overloading across namespaces is that this is the simplest rule (“the usual overloading rules apply”), and it is the only rule I can think of that allows us to migrate existing libraries to use namespaces with minimal source code changes. For example:

// old code:

    void f(int);     // from A.h
    // . . .

    void f(char);    // from B.h
    // . . .

    void g()
    {
        f('a'); // calls the f from B.h
    }

can be upgraded to the version using namespaces shown above without changing anything but the header files.

17.4.5.4 Nesting of Namespaces

One obvious use of namespaces is to wrap a complete set of declarations and definitions in a separate namespace:

namespace X {
     // all my declarations
}

The list of declarations will in general contain namespaces. Thus, for practical reasons – as well as for the simple reason that constructs ought to nest unless there is a strong reason for them not to – nested namespaces are allowed. For example:

void h();

namespace X {
     void g() ;
     // . . .
     namespace Y {
         void f() ;
         void ff();
         // ...
     }
     // ...

}

The usual scope and qualification rules apply:

void X::Y::ff()
{
    f () ; g () ; h ();
}

void X::g()
{
   f (); // error: no f() in X
   Y::f ();
}

void h()
{
    f () ;     // error: no global f()
    Y::f();    // error: no global Y
    X::f();    // error: no f() in X
    X::Y::f();
}

17.4.5.5 Namespaces are Open

A namespace is open; that is, you can add names to it from several namespace declarations. For example:

namespace A {
    int f(); // now A has member f()
};

namespace A {
    int g(); // now A has two members f() and g()
}

The aim was to support large program fragments within a single namespace the way a current library or application lives within the single global namespace. To do this, it is necessary to distribute the namespace definition over several header and source code files. This openness was also seen as a transition aid. For example:

// my header:
    extern void f(); // my function
    // ...
    #include<stdio.h>
    extern int g(); // my function
    // ...

can be rewritten without reordering of the declarations:

// my header:

    namespace Mine {
        void f(); // my function
        // ...
    }

    #include<stdio.h>

    namespace Mine {
        in g(); // my function
        // ...
    }

Current taste (including mine) favors the use of many smaller namespaces over putting really major pieces of code into a single namespace. That style could be enforced by requiring all members to be declared in a single namespace declaration in the same way all members of a class must be declared in a single class declaration. However, I saw no point in foregoing the many small conveniences I find with open namespaces in favor of a more restrictive system just to conform to some current taste.

17.5 Implications for Classes

It has been suggested that a namespace should be a kind of class. I don’t think that is a good idea because many class facilities exist exclusively to support the notion of a class being a user-defined type. For example, facilities for defining the creation and manipulation of objects of that type has little to do with scope issues.

The opposite, that a class is a kind of namespace, seems almost obviously true. A class is a namespace in the sense that all operations supported for namespaces can be applied with the same meaning to a class unless the operation is explicitly prohibited for classes. This implies simplicity and generality, while minimizing implementation effort. I consider this view vindicated by the smooth way namespaces fit into C++ and because solutions to apparently unrelated long-standing problems naturally follow from the basic namespace mechanisms.

17.5.1 Derived Classes

Consider the old problem of a class member hiding a member of the same name in a base class:

class B {
public:
    f(char);
};

class D : public B {
public:
    f(int); // hides f(char)
};

void f(D& d)
{
    d.f('c'); // calls D::f(int)
}

Naturally, the introduction of namespaces doesn’t change the meaning of such examples. A new explanation is possible, though: Because D is a class, the scope it provides is a namespace. The namespace D is nested in the namespace B, so D::f(int) hides B::f(char). Consequently, D::f(int) is called. If this resolution isn’t what is wanted, we can use a using-declaration to bring B’s f() into scope:

scope:

class B {
public:
    f(char);
};

class D : public B {
public:
    f(int);
    using B::f; // bring B::f into D to enable overloading
};

void f(D& d)
{
    d.f('c'); // calls D::f(char) !
}

We suddenly have a choice!

As ever, names from different sibling base classes can cause ambiguities (independently of what they name):

struct A { void f(int); };
struct B { void f(double); };

struct C : A, B {
    void g() {
        f(l);    // error: A::f(int) or B::f(double)
        f(1.0);  // error: A::f(int) or B::f(double)
    }
};

However, if we want to resolve these ambiguities, we can now do so by adding a couple of using-declarations to bring A::f and B::f into the scope of C:

struct C : A, B {
    using A::f;
    using B:: f;

    void g() {
        f(1);     // A::f(1)
        f(1.0);   // B::f(1.0)
    }
};

An explicit mechanism along these lines has been suggested repeatedly over the years. I remember discussing the possibility with Jonathan Shopiro while working on release 2.0, but rejecting it as being “too specialized and unique” to include. The using-declaration, on the other hand, is a general mechanism that just happens to provide a solution to this problem.

17.5.2 Using Base Classes

To avoid confusion, a using-declaration that is a class member must name a member of a (direct or indirect) base class. To avoid problems with the dominance rule (§12.3.1) using-directives are not allowed as class members.

struct D : public A {
    using namespace A; // error: using-directive as member
    using ::f; // error:::f not a member of a base class
};

A using-declaration naming a base class member has an important role to play in adjusting access:

class B {
public:
    f(char);
};

class D : private B {
public:
    using B: if.;
};

This achieves in a general and more obvious way what access-declarations2.10) were introduced to do:

class D : private B {
public:
    B::f; // old way: access declaration
};

Thus, using-declarations make the specialized access-declarations redundant. Consequently, access-declarations are deprecated. That is, access-declarations are slated to be removed sometime in the distant future after users have had ample time to upgrade.

17.5.3 Eliminating Global static

It is often useful to wrap a set of declarations in a namespace simply to avoid interference from declarations in header files or to avoid having the names used interfere with global declarations in other compilation units. For example:

#include <header.h>
namespace Mine {
    int a;
    void f() { /* ... */ }
    int g() { /* ... */ }
}

In many cases, we aren’t really interested in the name of the namespace as long as it doesn’t clash with other namespace names. To serve that need more elegantly, we allow a namespace to be unnamed:

#include <header.h>
namespace {
    int a;
    void f() { /* ... */ }
    int g() { /* ... */ }
}

Except for overloading by names in the header, this is equivalent to

#include <header.h>

static int a;
static void f() {/*...*/ }
static int g() {/*...*/}

Such overloading is usually undesirable, but easy to achieve when desired:

namespace {
#include <header.h>
    int a;
    void f() { /* ... */ }
    int g() {/*...*/ }
}

Thus, the namespace concept allows us to deprecate the use of static for control of visibility of global names. That leaves static with a single meaning in C++: statically allocated, don’t replicate.

The unnamed namespace is just like any other namespace except that we don’t need to utter its name. Basically,

namespace { /* ... */ }

is semantically equivalent to

namespace unique_name { /* ... */ }
using namespace unique_name;

Every unnamed namespace in a single scope share the same unique name. In particular, all global unnamed namespaces in a single translation unit are part of the same namespace and differ from the global unnamed namespace of other translation units.

17.6 C Compatibility

A function with C linkage can be placed in a namespace:

namespace X {
    extern "C" void f(int);
    void g(int)
}

This allows functions with C linkage to be used like other members of a namespace. For example:

void h()
{
    X::f ();
    X::g();
}

However, in a single program one cannot have two different functions with C linkage and the same name in different namespaces; both would resolve to the same C function. The unsafe rules of C linkage make such errors hard to find.

One alternative to this design would be to disallow functions with C linkage in namespaces. That would lead to disuse of namespaces by forcing people who need to interface to C to pollute the global namespace. This non-solution was deemed unacceptable.

Another alternative would be to ensure that two functions of the same name in different namespaces were really different functions even if they had C linkage. For example:

namespace X {
    extern "C" void f(int);
}

namespace Y {
    extern "C" void f(int);
}

The problem is then how to call such a function from a C program. Since the C language doesn’t have a mechanism for disambiguating based on namespaces, we would have to rely on an (almost certainly implementation-dependent) naming convention. For example, the C program might have to refer to __X__f and __Y__f. This solution was deemed unacceptable, so we stuck with the unsafe C rules. C pollutes the linker’s namespace, but not the global namespace of a C++ translation unit.

Note that this is a C problem (a compatibility hack) and not a problem with C++ namespaces. Linking to a language that has a mechanism analogous to C++’s name-spaces should be obvious and safe. For example, I’d expect this

namespace X {
    extern "Ada" void f(int);
}

namespace Y {
    extern "Ada" void f(int);
}

to be the way for a C++ program to map to functions in different Ada packages.

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

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