C++ now adds the ability to create named namespaces by defining a new kind of declarative region, one whose main purpose is to provide an area in which to declare names. The names in one namespace don’t conflict with the same names declared in other namespaces, and there are mechanisms for letting other parts of a program use items declared in a namespace. The following code, for example, uses the new keyword namespace
to create two namespaces, Jack
and Jill
:
namespace Jack {
double pail; // variable declaration
void fetch(); // function prototype
int pal; // variable declaration
struct Well { ... }; // structure declaration
}
namespace Jill {
double bucket(double n) { ... } // function definition
double fetch; // variable declaration
int pal; // variable declaration
struct Hill { ... }; // structure declaration
}
Namespaces can be located at the global level or inside other namespaces, but they cannot be placed in a block. Thus, a name declared in a namespace has external linkage by default (unless it refers to a constant).
In addition to user-defined namespaces, there is one more namespace, the global namespace. This corresponds to the file-level declarative region, so what used to be termed global variables are now described as being part of the global namespace.
The names in any one namespace don’t conflict with names in another namespace. Thus, the fetch
in Jack
can coexist with the fetch
in Jill
, and the Hill
in Jill
can coexist with an external Hill
. The rules governing declarations and definitions in a namespace are the same as the rules for global declarations and definitions.
Namespaces are open, meaning that you can add names to existing namespaces. For example, the following statement adds the name goose
to the existing list of names in Jill
:
namespace Jill {
char * goose(const char *);
}
Similarly, the original Jack
namespace provides a prototype for a fetch()
function. You can provide the code for the function later in the file (or in another file) by using the Jack
namespace again:
namespace Jack {
void fetch()
{
...
}
}
Of course, you need a way to access names in a given namespace. The simplest way is to use ::
, the scope-resolution operator, to qualify a name with its namespace:
Jack::pail = 12.34; // use a variable
Jill::Hill mole; // create a type Hill structure
Jack::fetch(); // use a function
An unadorned name, such as pail
, is termed the unqualified name, whereas a name with the namespace, as in Jack::pail
, is termed a qualified name.
using
Declarations and using
DirectivesHaving to qualify names every time they are used is not always an appealing prospect, so C++ provides two mechanisms—the using
declaration and the using
directive—to simplify using namespace names. The using
declaration lets you make particular identifiers available, and the using
directive makes the entire namespace accessible.
The using
declaration involves preceding a qualified name with the keyword using
:
using Jill::fetch; // a using declaration
A using
declaration adds a particular name to the declarative region in which it occurs. For example, the using
declaration of Jill::fetch
in main()
adds fetch
to the declarative region defined by main()
. After making this declaration, you can use the name fetch
instead of Jill::fetch
. The following code fragment illustrates these points:
namespace Jill {
double bucket(double n) { ... }
double fetch;
struct Hill { ... };
}
char fetch;
int main()
{
using Jill::fetch; // put fetch into local namespace
double fetch; // Error! Already have a local fetch
cin >> fetch; // read a value into Jill::fetch
cin >> ::fetch; // read a value into global fetch
...
}
Because a using
declaration adds the name to the local declarative region, this example precludes creating another local variable by the name of fetch
. Also like any other local variable, fetch
would override a global variable by the same name.
Placing a using
declaration at the external level adds the name to the global namespace:
void other();
namespace Jill {
double bucket(double n) { ... }
double fetch;
struct Hill { ... };
}
using Jill::fetch; // put fetch into global namespace
int main()
{
cin >> fetch; // read a value into Jill::fetch
other()
...
}
void other()
{
cout << fetch; // display Jill::fetch
...
}
A using
declaration, then, makes a single name available. In contrast, the using
directive makes all the names available. A using
directive involves preceding a namespace name with the keywords using namespace
, and it makes all the names in the namespace available without the use of the scope-resolution operator:
using namespace Jack; // make all the names in Jack available
Placing a using
directive at the global level makes the namespace names available globally. You’ve seen this in action a few times in this book in the following form:
#include <iostream> // places names in namespace std
using namespace std; // make names available globally
Placing a using
directive in a particular function makes the names available just in that function. Here’s an example:
int main()
{
using namespace jack; // make names available in vorn()
...
}
You’ve seen this form often in this book with the std
namespace.
One thing to keep in mind about using
directives and using
declarations is that they increase the possibility of name conflicts. That is, if you have both namespace jack
and namespace jill
available, and you use the scope-resolution operator, there is no ambiguity:
jack::pal = 3;
jill::pal =10;
The variables jack::pal
and jill::pal
are distinct identifiers for distinct memory locations. However, if you employ using
declarations, the situation changes:
using jack::pal;
using jill::pal;
pal = 4; // which one? now have a conflict
In fact, the compiler won’t let you use both of these using
declarations because of the ambiguity that would be created.
using
Directives Versus using
DeclarationsUsing a using
directive to import all the names from a namespace wholesale is not the same as using multiple using
declarations. It’s more like the mass application of a scope-resolution operator. When you use a using
declaration, it is as if the name is declared at the location of the using
declaration. If a particular name is already declared in a function, you can’t import the same name with a using
declaration. When you use a using
directive, however, name resolution takes place as if you declared the names in the smallest declarative region containing both the using
declaration and the namespace itself. For the following example, that would be the global namespace. If you use a using
directive to import a name that is already declared in a function, the local name will hide the namespace name, just as it would hide a global variable of the same name. However, you can still use the scope-resolution operator, as in the following example:
namespace Jill {
double bucket(double n) { ... }
double fetch;
struct Hill { ... };
}
char fetch; // global namespace
int main()
{
using namespace Jill; // import all namespace names
Hill Thrill; // create a type Jill::Hill structure
double water = bucket(2); // use Jill::bucket();
double fetch; // not an error; hides Jill::fetch
cin >> fetch; // read a value into the local fetch
cin >> ::fetch; // read a value into global fetch
cin >> Jill::fetch; // read a value into Jill::fetch
...
}
int foom()
{
Hill top; // ERROR
Jill::Hill crest; // valid
}
Here, in main()
, the name Jill::fetch
is placed in the local namespace. It doesn’t have local scope, so it doesn’t override the global fetch
. But the locally declared fetch
hides both Jill::fetch
and the global fetch
. However, both of the last two fetch
variables are available if you use the scope-resolution operator. You might want to compare this example to the preceding one, which uses a using
declaration.
One other point of note is that although a using
directive in a function treats the namespace names as being declared outside the function, it doesn’t make those names available to other functions in the file. Hence in the preceding example, the foom()
function can’t use the unqualified Hill
identifier.
Suppose a namespace and a declarative region both define the same name. If you attempt to use a using
declaration to bring the namespace name into the declarative region, the two names conflict, and you get an error. If you use a using
directive to bring the namespace name into the declarative region, the local version of the name hides the namespace version.
Generally speaking, the using
declaration is safer to use than a using
directive because it shows exactly what names you are making available. And if the name conflicts with a local name, the compiler lets you know. The using
directive adds all names, even ones you might not need. If a local name conflicts, it overrides the namespace version, and you aren’t warned. Also the open nature of namespaces means that the complete list of names in a namespace might be spread over several locations, making it difficult to know exactly which names you are adding.
This is the approach used for most of this book’s examples:
#include <iostream>
int main()
{
using namespace std;
First, the iostream
header file puts everything in the std
namespace. Then, the using
directive makes the names available within main()
. Some examples do this instead:
#include <iostream>
using namespace std;
int main()
{
This exports everything from the std
namespace into the global namespace. The main rationale for this approach is expediency. It’s easy to do, and if your system doesn’t have namespaces, you can replace the first two of the preceding code lines with the original form:
#include <iostream.h>
However, namespace proponents hope that you will be more selective and use either the scope-resolution operator or the using
declaration. That is, you shouldn’t use the following:
using namespace std; // avoid as too indiscriminate
int x;
std::cin >> x;
std::cout << x << std::endl;
Or you could use this:
using std::cin;
using std::cout;
using std::endl;
int x;
cin >> x;
cout << x << endl;
You can use nested namespaces, as described in the following section, to create a namespace that holds the using
declarations you commonly use.
You can nest namespace declarations, like this:
namespace elements
{
namespace fire
{
int flame;
...
}
float water;
}
In this case, you refer to the flame
variable as elements::fire::flame
. Similarly, you can make the inner names available with this using
directive:
using namespace elements::fire;
Also you can use using
directives and using
declarations inside namespaces, like this:
namespace myth
{
using Jill::fetch;
using namespace elements;
using std::cout;
using std::cin;
}
Suppose you want to access Jill::fetch
. Because Jill::fetch
is now part of the myth
namespace, where it can be called fetch
, you can access it this way:
std::cin >> myth::fetch;
Of course, because it is also part of the Jill
namespace, you still can call it Jill::fetch
:
std::cout << Jill::fetch; // display value read into myth::fetch
Or you can do this, provided that no local variables conflict:
using namespace myth;
cin >> fetch; // really std::cin and Jill::fetch
Now consider applying a using
directive to the myth namespace. The using
directive is transitive. We say that an operation op is transitive if A op B and B op C implies A op C. For example, the >
operator is transitive. (That is, A bigger than B and B bigger than C implies A bigger than C.) In this context, the upshot is that the following statement places both the myth
and the elements
namespaces in scope:
using namespace myth;
This single directive has the same effect as the following two directives:
using namespace myth;
using namespace elements;
You can create an alias for a namespace. For example, suppose you have a namespace defined as follows:
namespace my_very_favorite_things { ... };
You can make mvft
an alias for my_very_favorite_things
by using the following statement:
namespace mvft = my_very_favorite_things;
You can use this technique to simplify using nested namespaces:
namespace MEF = myth::elements::fire;
using MEF::flame;
You can create an unnamed namespace by omitting the namespace name:
namespace // unnamed namespace
{
int ice;
int bandycoot;
}
This code behaves as if it were followed by a using
directive; that is, the names declared in this namespace are in potential scope until the end of the declarative region that contains the unnamed namespace. In this respect, names in an unnamed namespace are like global variables. However, if a namespace has no name, you can’t explicitly use a using
directive or using
declaration to make the names available elsewhere. In particular, you can’t use names from an unnamed namespace in a file other than the one that contains the namespace declaration. This provides an alternative to using static variables with internal linkage. Suppose, for example, you have this code:
static int counts; // static storage, internal linkage
int other();
int main()
{
...
}
int other()
{
...
}
The namespace approach is to do this instead:
namespace
{
int counts; // static storage, internal linkage
}
int other();
int main()
{
...
}
int other()
{
...
}
3.128.200.220