9.12. Namespaces And The Scope Resolution Operator

Time: 00:05:10 | Download: Large, Large (CC), Small | Streaming, Streaming (CC) | Slides: PDF, PPTX
Review
Namespace

"A namespace is a declarative region that provides a scope to the identifiers (the names of types, functions, variables, etc) inside it. Namespaces are used to organize code into logical groups and to prevent name collisions that can occur especially when your code base includes multiple libraries" (Microsoft Documentation).

Chapter 1 introduced the standard namespace, std, and illustrated two options for accessing the I/O classes it declares:

#include <iostream>
using namespace std;
	.
	.
	.
cout << "hello world" << endl;
#include <iostream>
	.
	.
	.
std::cout << "hello world" << std::endl;
 
(a)(b)
namespace notation. Declaring a named entity inside a namespace gives it an extended, qualified name. For example, programs form the fully qualified names of cout and endl by prepending the namespace name and the scope resolution operator.
  1. The using statement forms a shorthand notation for accessing the features declared in a namespace without using the fully qualified name.
  2. Programs can access the entities declared in a namespace using a fully qualified name consisting of the namespace and entity name joined with the scope resolution operator.

All the example programs presented throughout the text and all the programming assignments you have completed depend on the std namespace. Despite using namespaces, we haven't explored how to create them or why they are helpful. This section addresses those omissions.

The namespace Syntax

To understand how namespaces work and why we need them, we must understand what a "name collision" means. Linguists estimate that there are more than a million words in English, with similar numbers in other languages. So, it seems like we should be able to write a program without running out of identifier names. For example, using English's 26-character alphabet, we can form nearly 457,000 four-character patterns, almost half the number of words in the language. But most of those patterns are meaningless. Furthermore, programmers often name programming elements based on their role in a program (i.e., on what they do). This practice sets the stage for potential name collisions or name conflicts.

It's easy to resolve name collisions by changing one of the colliding names when a programming team works together to create a system. But when we create a system incorporating multi-sourced components delivered as executables and object-code libraries (Figure 4, step 3), it's not possible to change conflicting names. I encountered this problem in a C program integrating a database management system with library code licensed by different vendors. In C++, namespaces solve this problem by creating named scopes.

Vendors avoid name collisions by declaring classes, non-member functions, and global variables in uniquely named namespaces with names typically based on corporate names, which are trademarked and, therefore, unique. Namespaces included in the C++ API are managed and reserved by the ANSI C++ committee. In C++, the scope resolution operator binds the namespace name to the names of its enclosed entities. The name "scope resolution operator" suggests that it helps the compiler determine the scope of otherwise ambiguous programming elements.

namespace Acme
{
    void print(string name);
};

namespace Widgets
{
    void print(string part);
};
void Acme::print(string name)
{
    cout << "Acme " << name << endl;
};

void Widgets::print(string part)
{
    cout << "Widgets " << part << endl;
}
(a)(b)
int main()
{
    Acme::print("Dilbert");
    Widgets::print("wing nut");
    return 0;
}
// Error - a namespace is an invalid type name

Acme m;
Widgets* w = new Widgets;

 
(c)(d)
namespace and the scope resolution operator. The simplified code introduces the "namespace" keyword and how to create, fill, and use namespace structures. The scope resolution operator (displayed in red) has two operands. The left-hand operand is a namespace name, and the right-hand operand is the name of a feature declared in it. The operator binds the feature to the namespace's scope.
  1. Namespaces are syntactically similar to classes and structures; like them, they create a new, named scope.
  2. The namespace is included with and attached to the function definition with the scope resolution operator. Although not illustrated here, we can include small function bodies in the namespace.
  3. When client code calls a namespace function, it must use the namespace name and the scope resolution operator to form the complete function name.
  4. However, unlike classes and structures, programmers cannot create variables or objects from a namespace.
The figure illustrates a name collision or conflict with non-member functions, but conflicts can also occur with classes and global variables.

 

class beta
{
    public:
        string to_string(int value)
            { return std::to_string(value); }
};
namespace std
{
	string to_string(int value);
};

 
(a)(b)
Clarifying ambiguous scope with the scope resolution operator. Programs may contain non-conflicting, overloaded functions (functions with the same name), but proper overloading requires that they have unique parameter lists. In situations like the function chaining example illustrated in (a), it's desirable to have functions with the same name and parameters. These functions are not overloaded, and their names conflict without clarification.
  1. Class beta defines a to_string member function that calls the C++ string class to_string function (see string To Number and Number To string). Without the namespace name and the scope resolution operator, the call becomes a direct recursion function call. Programmers use the scope resolution operator to resolve the ambiguous use of function names, as illustrated here by to_string.
  2. C++ declares the common or standard C++ library functions in the std namespace, and programs can usually access them without ambiguity just by adding the "using" statement at the beginning of a file.

Programming Namespaces

As the size and complexity of programs increase, programmers need some way to organize and manage namespaces. Namespaces are declarative structures like prototypes and class specifications. They are also containers that wrap and extend the names of functions and classes. Programmers incorporate namespaces in header files to wrap prototypes and class specifications. The following examples demonstrate how programmers add namespaces to function prototypes and class specifications in header files.

Acme.hAcme.cppClient
namespace Acme
{
    void f1();
    int f2();
};
#include "Acme.h"

void Acme::f1()
{
    ...
}

int Acme::f2()
{
    ...
}
#include "Acme.h"

namespace Acme
{
    void f1()
    {
        ...
    }

    int f2()
    {
        ...
    }
}
#include <iostream>
#include "Acme.h"
using namespace std;
using namespace Acme;

int main()
{
    f1();
    f2();

    return 0;
}
(a)(b)(c)(d)
Putting functions in a namespace. Together, Acme.h and Acme.cpp form a supplier. Only a modest amount of code is needed to add namespaces to suppliers and clients.
  1. The namespace's body is a block formed by a pair of braces that introduce a new scope. Putting the prototypes in the namespace binds the function name to the namespace.
  2. Programmers continue defining functions in source code files that include the namespace header file. The definitions must include the fully qualified names consisting of the namespace and function names.
  3. An alternate syntax opens the namespace to add the function definitions rather than using the fully qualified function names.
  4. A client or application program includes and uses the namespace, allowing it to call the namespace functions. Alternatively, the client can include the namespace but call the functions with their qualified names, Acme::f1(); and Acme::f2();, rather than employing the "using" statement.
In large programs, especially when the supplier forms a separate library, the client and supplier are unlikely to be colocated in the same directory. In that case, the programmers modify the #include "Acme.h" directive in the client to include the supplier's path. For example, #include "dir1/dir2/Acme.h" or #include "/acme/Acme.h". Notice that the forward slash character is the path separator, even on Windows platforms. The text revisits and extends pathnames in chapter 14, File System Organization.

 

Acme.hfoo.hbar.hclient
namespace Acme
{
    class foo;
    class bar;
};
#include "Acme.h"

/*namespace Acme
{
    class foo
    {
        public:
            foo() { ... }
    };
};*/

class Acme::foo
{
    public:
        foo() { ... }
};
#include "Acme.h"

/*namespace Acme
{
    class bar
    {
        public:
            bar() { ... }
    };
};*/

class Acme::bar
{
    public:
        bar() { ... }
};
#include "Acme.h"
#include "foo.h"
#include "bar.h"
using namespace Acme;

int main()
{
    foo f;
    bar b;

    return 0;
}
(a)(b)(c)
Putting classes in a namespace. Programs can open and close namespaces without limit. Each time a program opens them, it adds new declarations without removing or affecting those already in the namespace. This process allows programs and libraries to build large namespaces from smaller, more manageable files.
  1. Declaring a class inside a namespace binds the class to the namespace. As illustrated in this example, the keyword class creates a forward declaration for classes foo and bar.
  2. This organization allows programmers to continue specifying each class in a separate header file while still putting them in the same namespace. Programmers can specify the full class name in the namespace (as in the commented-out example) or use the fully qualified class name to specify the class. Programmers still put the member function definitions in separate source code files, but reference them with fully qualified names.
  3. Clients must #include the class header files (foo.h and bar.h), but including the namespace header (Acme.h) is optional because the class headers include it. This example demonstrates the using statement, but programmers may omit it and use the fully qualified class names: Acme::foo and Acme::bar instead.

The Pros And Cons Of The using Statement

Figure 1 illustrates two options for accessing namespace entities. The programming examples appearing throughout the textbook illustrate my preference for option (a), but this choice is not universal. Odumosu Matthew outlines three objections to the using namespace std; option, and extends those argument to the using statement generally.

He recommends, as a better practice, explicitly naming the namespace, as in Figure 1(b), when using an entity, suggesting that the practice will reduce these problems. His objections are worthy of consideration.

Namespace Pollution

The section opens with a definition suggesting that namespaces declare "the names of types, functions, variables, etc." Specifically, namespaces declare classes, non-member functions, and global variables. Figure 2 illustrates namespaces with non-member functions, so the text describes the following examples with classes without loss of generality. I argue that two architectural organizations and one fallback method can realize the same benefits while avoiding excessive coding notations.

The primary way C++ programmers limit class name conflicts is by specifying each class in a separate header file, as demonstrated by the Time and fraction classes. This organization affords programmers considerable namespace control using a familiar mechanism: the #include preprocessor directive. A simple example demonstrates how the directive helps reduce name conflicts.

#include <iostream>
//#include <iomanip>				// (i)
#include "setw.h"				// (ii)

using namespace std;

int main()
{
    cout << setw(20) << "Hello World\n";	// (iii)

    return 0;
}
class setw
{
    private:
        int wid;
    public:
        setw(int w) : wid(w) {}
        friend ostream& operator<<(ostream& out, setw& w)
        {
            out.width(w.wid);
            return out;
        }
};
(a)(b) - setw.h
Name declaration vs. class specification. Namespaces and header files have related but distinct purposes.
  1. Although the name setw is declared in the std namespace, its specification is located in the <iomanip> header file.
    1. The example program uses the standard namespace. However, if it doesn't #include <iomanip>, it can't "see" the setw specification, and fails to compile.
    2. Alternatively, programmers can create their own setw manipulator, placing it in an appropriately named header file.
    3. The setw manipulator the program uses depends on the included header file.
  2. A non-standard setw manipulator that does not conflict with the version in the standard namespace. Chapter 11 discusses how to create new versions of operators, such as <<. However, focusing on the class name is sufficient to understand the example.

Architecture 2: Components

Complex object-oriented programs consist of many objects, instantiated from numerous classes, each contributing to the solution of the program's overall problem. As programs grow in size and complexity, it becomes convenient to organize classes into functional units. An early analysis and design process termed the units components, and named four standard occurrences. Components help illustrate the second architectural organization, helping to reduce name conflicts.

The four standard components. The Coad & Yourdon1 components (p. 26) group four well-defined program tasks. The classes within each component share its responsibilities. Furthermore, the names of well-defined classes reflect their functional purpose. Therefore, the class names in a component might be similar, but they will be distinct to reflect their varying responsibilities, reducing or eliminating intra-component class name conflicts. The distinct purposes of the components reduce the likelihood of inter-component class name conflicts.

The UML has largely supplanted the Coad & Yourdon analysis and design method. It acknowledges that complex programs often contain more than four functional units. The UML2 (pp. 163-174) renames components as packages, and doesn't specify their number, purpose, or names.

Fallback: Resolving Name Conflicts

In a program rigorously implemented with the object-oriented paradigm, classes provide sufficiently distinct scopes for most functions and variables. So, adhering to the above architectural organizations significantly reduces the likelihood of name collisions or conflicts in a program. However, they cannot guarantee elimination. If a name conflict occurs in a program, programmers can always fall back to using the fully qualified name consisting of the namespace and the entity names.

namespace Acme
{
    class foo;
    class dlilbert;
};


namespace Widgets
{
    class foo;
    class wally;
};
#import "/Acme/foo.h"
#import "/Widgets/foo.h"
using namespace Acme;
using namespace Widgets;

int main()
{
    Acme::foo f1;
    Widgets::foo f2;
    dilbert d;
    wally w;
    return 0;
}
(a)(b)
Disambiguating name conflicts. Name conflicts generally arise when a client program combines library or supplier code from multiple vendors. Each vendor names their functions or classes without regard for others' names.
  1. This example illustrates two namespaces, each originating with a different vendor. Both namespaces declare a class named foo.
  2. Programs using both namespaces will experience a collision, but client programmers can clarify or disambiguate the conflict with fully qualified class names as Matthew recommends. Rather than use fully qualified class names throughout a program, I prefer to use them when necessary and rely on the more compact notation otherwise. Notice that the using statement still allows access to non-conflicting names in both namespaces.

Readability And Maintenance

Where name conflicts are unequivocally programming errors - they prevent the program from compiling - readability is more subjective. The subjectivity doesn't diminish the importance of readability, but it does make assessing different approaches more challenging: code one programmer finds readable, another may not. std::cout undeniably conveys more information to a reader than cout, but the significance of the additional information is arguable. Programmers with a moderate level of experience know that cout is specified in the std namespace. Classes specified in specialized namespaces appear infrequently in programs and are limited to specific packages. In the case of specialized namespaces, a suitable comment will clarify any ambiguities, while still allowing the program to use a compact notation.

The lifespans of software systems vary significantly, with some systems operating for decades. The longer a system persists, the more likely it is to need maintenance: extension, adaptation, and correction. While readable code is essential for maintainable software systems, maintainability entails more than choosing how programmers access entities specified in a namespace. Maintainability rests on robust, well-designed data structures that isolate and control access to the stored data. It further requires tight, cohesive functional units with minimal inter-unit connections that limit and localize the effects of internal changes. The architectural organizations described above implement these characteristics, enhancing maintainability far more than explicitly naming the namespace specifying an entity.

using Statement Conclusions

Programmers working for an institution that has programming standards should follow those standards. Students submitting programs for scoring or evaluation should follow the instructor's standards. In the absence of explicit standards or when programmers create their own product, they create and follow their personal style: whether they favor or disfavor the using statement is a matter of taste.


  1. Coad, P. & Yourdon, E. (1991). Object-oriented design. Yourdon Press.
  2. Booch, G., Rumbaugh, J., & Jacobson, I. (2005). The unified modeling language (2nd ed.). Addison-Wesley.