Previous | Table of Contents | Next |
After several false starts, a facility for defining and using namespaces was introduced into C++ in 1994. In the standards committee, the initial impetus came from a proposal written by Keith Rowe from Microsoft. By early 1993, I hadwith the help of multimegabyte email exchanges and discussions at the standards meetingssynthesized a coherent proposal. I recall technical contributions on namespaces from Dag Bruck, John Bruns, Steve Dovich, Bill Gibbons, Philippe Gautron, Tony Hansen, Peter Juhl, Andrew Koenig, Eric Krohn, Doug McIlroy, Richard Minner, Martin ORiordan, John Skaller, Jerry Schwarz, Mark Terribile, and Mike Vilot.
The basic idea is simple: A name can be declared in a named scopecalled a namespaceand to use a name from outside its namespace, some explicit action is needed. By default, this eliminates name clashes. The problem is to make access so convenient that people do not reject namespaces as not worth the effort to use them.
For example, here are some facilities for dealing with time that have been defined in their own namespace Chrono to establish the fact that they constitute a logical explicit unit and to avoid name clashes:
namespace Chrono { // facilities for dealing with time class Date { // ... }; int operator-(Date a, Date b); bool leapyear(int y); Date next_weekday(Date d); Date next_saturday(Date d); class Time { // ... }; // ... }
A programmer can refer to a name from a namespace by explicit qualification:
void f(Chrono::Date d) { Chrono::Date monday = Chrono::next_saturday(d).add_day(2); // ... }
However, I saw no hope of programmers accepting a notation that verbose and redundant. In particular, there would be no way of gradually introducing namespaces into existing programs. Consequently, we provided mechanisms for making a namespace name implicit. A using declaration introduces a local synonym into a scope:
using Chrono::Date; // Date is Chrono::Date void f(Date d) { Date monday = Chrono::next_saturday(d).add_day(2); // ... }
A using directive is a somewhat more powerful and therefore potentially more dangerous mechanism that makes every name from a namespace available in a scope:
void g(Chrono::Date d) { using namespace Chrono; // make Chrono names available Date monday = next_saturday(d).add_day(2); }
The using directive is intended primarily to smooth the introduction of namespaces into existing programs where there is no explicit mention of namespaces.
Andrew Koenig observed that in many cases, no explicit resolution mechanism was needed if we resolved a function call or the use of an operator based on the namespace (or namespaces) of the operands. Heres an example:
void f(Chrono::Date d) { Chrono::Date monday = next_saturday(d).add_day(2); if (monday-d < 2) { // ... } // ... }
Here, next_saturday() and the operator defined in Chrono are used because those are the ones logically connected with the operands by being declared in Chrono. For further details, see The Design and Evolution of C++ (Stroustrup, 1994) and The C++ Programming Language, Third Edition (Stroustrup, 1997).
In the original C++ design, I deliberately didnt include the Simula mechanisms for runtime type identification (QUA and INSPECT). In my experience, they were almost always misused so that the benefits from having the mechanism would be outweighed by the disadvantages. However, in large systems composed of separately developed parts, it is often unavoidable that information is passed around in ways that renders their exact type unknown. Thus, I reluctantly accepted that the time to introduce a mechanism for runtime type identification had come. The immediate impetus was work by Dmitry Lenkov and his colleagues at HP (Lenkov, Mehta, & Unni, 1991). Lenkov and I developed a proposal for a standard mechanism for runtime type identification where the primary operation is a runtime-checked pointer conversion. Heres an example:
void my_fct(Dialog_box* bp) { if (My_dbox* dbp = dynamic_cast<My_dbox*>(bp)) { // use dbp } else { // treat *pb as a plain dialog box } }
The dynamic_cast<My_dbox*>(bp) operator converts its operand bp to the desired type My_dbox* if *bp really is a My_dbox or a class derived from My_dbox; otherwise, the value of dynamic_cast<My_dbox*>(bp) is 0.
Thus, the dynamic_cast operator combines a test of the actual type of the object taking inheritance into account (like Smalltalks isKindOf) with a pointer conversion. For a language that relies on static type checking, the conversion is necessary, and not taking inheritance into account would render the operation clumsy and error-prone. The use of an ugly conversion notation and the absence of a type-switch statement (like Simulas INSPECT) is intended to minimize (mis)use of runtime type information to write programs that violate modularity by centralizing information about otherwise unrelated types in type switches.
For further details, see The Design and Evolution of C++ (Stroustrup, 1994) and The C++ Programming Language, Third Edition (Stroustrup, 1997).
Previous | Table of Contents | Next |