April 24, 2008, 10:35 a.m.
posted by nogood
Item 23. NamespacesGlobal scope was getting overly crowded. Everybody and his brother implemented libraries that reused the same names for different classes and functions. For example, many libraries wanted to include a class named String, but if you used two different libraries that defined a String type, you'd get a multiple definition error or worse. Various extra-language approaches used to address this problem (naming conventions, the preprocessor, ...) only made things worse. Namespaces to the rescue. In some ways, namespaces introduce complexity (see Argument Dependent Lookup [25, 89]), but most uses of namespaces are simple and simplifying. A namespace is a subdivision of global scope:
namespace org_semantics {
class String { ... };
String operator +( const String &, const String & );
// other classes, functions, typedefs, etc...
}
This code snippet opens a namespace called org_semantics, declares some useful things, and then closes the namespace with a closing curly brace. You can always add more to a namespace by just repeating the process; namespaces are "open." Note that some of the names in the org_semantics namespace are declared but not defined. To define these names, we can reopen the namespace:
namespace org_semantics {
String operator +( const String &a, String &b ) { // oops!
//...
}
}
Alternatively, we can simply qualify the definition with the namespace name without reopening the namespace:
org_semantics::String
org_semantics::operator +(
const org_semantics::String &a,
const org_semantics::String &b ) {
//...
}
This has the advantage of not allowing one to inadvertently declare a new namespace name (as we did when we left out the const in the second argument of our first definition of operator +) rather than define an already declared one. Admittedly, the seemingly endless repetition of org_semantics in this case can be tedious, but that's the price of security! We'll discuss some approaches that can improve this situation. If you want to use a name that's defined in a particular namespace, you have to tell the compiler in what namespace the name is to be found: org_semantics::String s( "Hello, World!" ); Although some of the C++ standard library has remained in global scope (the standard global operator news, operator deletes, array news, and array deletes come to mind), the bulk of the standard library is now resident in the std (that is, "standard") namespace, and most standard library use requires qualification with the std namespace name:
#include <iostream>
#include <vector>
//...
void aFunc() {
vector<int> a; // error! I don't see a vector!
std::vector<int> b; // Oh, there it is!
cout << "Oops!" << endl; // errors!
std::cout << "Better!" << std::endl; // OK
//...
}
Clearly, continual explicit qualification can be tedious. One way to relieve the tedium is to employ a "using directive."
void aFunc() {
using namespace std; // using directive
vector<int> a; // OK
cout << "Hello!" << endl; // OK
//...
}
A using directive essentially "imports" the names from the namespace, making them accessible without qualification in the scope of the using directive. In this case, the using directive is in force until the end of the function body, and then you're back to explicit qualification. For this reason, many C++ programmers (even many who should know better) suggest putting the using directive at global scope: #include <iostream> #include <vector> using namespace std; using namespace org_semantics; That's a bad idea. Now we're back nearly to square one, with all the names from a namespace available everywhere, sowing confusion and disarray. This is a particularly bad idea in a header file, where you can leverage your bad decision over any file that includes your header. In header files, we usually prefer to stick with explicit qualification and reserve using directives for smaller scopes (such as function bodies or blocks within a function) where their effects are bounded and easier to control. Basically, you have to be on your best behavior in header files and on pretty good behavior in source files, but you can kick back and relax inside a function. One interesting aspect of using directives is that they make the names of a namespace available, but as if they were declared at global scope, not necessarily at the scope in which the using directive occurs. Local names will hide namespace names:
void aFunc() {
using namespace std; // using directive
//...
int vector = 12; // a poorly named local variable...
vector<int> a; // error! std::vector is hidden
std::vector<int> b; // OK, can use explicit qualification
//...
}
An alternative is a "using declaration" that provides access to a namespace name through an actual declaration:
void aFunc() {
using std::vector; // using declaration
//...
int vector = 12; // error! redeclaration of vector
vector<int> a; // OK
//...
}
Using declarations are often a good middle ground between tedious explicit qualification and unrestrained use of using directives, particularly if a given section of code uses only a few names from two or more namespaces but uses them repeatedly:
void aFunc() {
using std::cout;
using std::endl;
using org_semantics::String;
String a, b, c;
//...
cout << a << b << c << endl;
// etc.
}
Another way to deal with long, tedious namespace names is to employ an alias: namespace S = org_semantics; Now S may be used in place of org_semantics within the scope of the alias. Like a using directive, a namespace alias is best avoided in a header file. (S is likely to conflict with other names more often than org_semantics, after all...) Let's finish up our quick tour of namespaces with a look at anonymous namespaces:
namespace {
int anInt = 12;
int aFunc() { return anInt; }
}
This anonymous namespace behaves identically to the following, where __compiler_generated_name__ is unique for each anonymous namespace:
namespace __compiler_generated_name__ {
int anInt = 12;
int aFunc() { return anInt; }
}
using namespace __compiler_generated_name__;
This is the trendy new way to avoid declaring functions and variables with static linkage. In the anonymous namespace above, both anInt and aFunc have external linkage, but they can be accessed only within the translation unit (that is, preprocessed file) in which they occur, just like a static. |
- Comment