Large programs tend to use independently developed libraries. Such libraries also tend to define a large number of global names, such as classes, functions, and templates. When an application uses libraries from many different vendors, it is almost inevitable that some of these names will clash. Libraries that put names into the global namespace are said to cause namespace pollution.
Traditionally, programmers avoided namespace pollution by using very long names for the global entities they defined. Those names often contained a prefix indicating which library defined the name:
class cplusplus_primer_Query { ... };
string cplusplus_primer_make_plural(size_t, string&);
This solution is far from ideal: It can be cumbersome for programmers to write and read programs that use such long names.
Namespaces provide a much more controlled mechanism for preventing name collisions. Namespaces partition the global namespace. A namespace is a scope. By defining a library’s names inside a namespace, library authors (and users) can avoid the limitations inherent in global names.
A namespace definition begins with the keyword namespace
followed by the namespace name. Following the namespace name is a sequence of declarations and definitions delimited by curly braces. Any declaration that can appear at global scope can be put into a namespace: classes, variables (with their initializations), functions (with their definitions), templates, and other namespaces:
namespace cplusplus_primer {
class Sales_data { / * ... * /};
Sales_data operator+(const Sales_data&,
const Sales_data&);
class Query { /* ... */ };
class Query_base { /* ... */};
} // like blocks, namespaces do not end with a semicolon
This code defines a namespace named cplusplus_primer
with four members: three classes and an overloaded +
operator.
As with any name, a namespace name must be unique within the scope in which the namespace is defined. Namespaces may be defined at global scope or inside another namespace. They may not be defined inside a function or a class.
As is the case for any scope, each name in a namespace must refer to a unique entity within that namespace. Because different namespaces introduce different scopes, different namespaces may have members with the same name.
Names defined in a namespace may be accessed directly by other members of the namespace, including scopes nested within those members. Code outside the namespace must indicate the namespace in which the name is defined:
cplusplus_primer::Query q =
cplusplus_primer::Query("hello");
If another namespace (say, AddisonWesley
) also provides a Query
class and we want to use that class instead of the one defined in cplusplus_primer
, we can do so by modifying our code as follows:
AddisonWesley::Query q = AddisonWesley::Query("hello");
As we saw in § 16.5 (p. 709), unlike other scopes, a namespace can be defined in several parts. Writing a namespace definition:
namespace nsp {
// declarations
}
either defines a new namespace named nsp
or adds to an existing one. If the name nsp
does not refer to a previously defined namespace, then a new namespace with that name is created. Otherwise, this definition opens an existing namespace and adds declarations to that already existing namespace.
The fact that namespace definitions can be discontiguous lets us compose a namespace from separate interface and implementation files. Thus, a namespace can be organized in the same way that we manage our own class and function definitions:
• Namespace members that define classes, and declarations for the functions and objects that are part of the class interface, can be put into header files. These headers can be included by files that use those namespace members.
• The definitions of namespace members can be put in separate source files.
Organizing our namespaces this way also satisfies the requirement that various entities—non-inline functions, static data members, variables, and so forth—may be defined only once in a program. This requirement applies equally to names defined in a namespace. By separating the interface and implementation, we can ensure that the functions and other names we need are defined only once, but the same declaration will be seen whenever the entity is used.
Namespaces that define multiple, unrelated types should use separate files to represent each type (or each collection of related types) that the namespace defines.
Using this strategy for separating interface and implementation, we might define the cplusplus_primer
library in several separate files. The declarations for Sales_data
and its related functions would be placed in Sales_data.h
, those for the Query
classes of Chapter 15 in Query.h
, and so on. The corresponding implementation files would be in files such as Sales_data.cc
and Query.cc
:
// ---- Sales_data.h----
// #includes should appear before opening the namespace
#include <string>
namespace cplusplus_primer {
class Sales_data { /* ... */};
Sales_data operator+(const Sales_data&,
const Sales_data&);
// declarations for the remaining functions in the Sales_data interface
}
// ---- Sales_data.cc----
// be sure any #includes appear before opening the namespace
#include "Sales_data.h"
namespace cplusplus_primer {
// definitions for Sales_data members and overloaded operators
}
A program using our library would include whichever headers it needed. The names in those headers are defined inside the cplusplus_primer
namespace:
// ---- user.cc----
// names in the Sales_data.h header are in the cplusplus_primer namespace
#include "Sales_data.h"
int main()
{
using cplusplus_primer::Sales_data;
Sales_data trans1, trans2;
// ...
return 0;
}
This program organization gives the developers and the users of our library the needed modularity. Each class is still organized into its own interface and implementation files. A user of one class need not compile names related to the others. We can hide the implementations from our users, while allowing the files Sales_data.cc
and user.cc
to be compiled and linked into one program without causing any compile-time or link-time errors. Developers of the library can work independently on the implementation of each type.
It is worth noting that ordinarily, we do not put a #include
inside the namespace. If we did, we would be attempting to define all the names in that header as members of the enclosing namespace. For example, if our Sales_data.h
file opened the cplusplus_primer
before including the string
header our program would be in error. It would be attempting to define the std
namespace nested inside cplusplus_primer
.
Assuming the appropriate declarations are in scope, code inside a namespace may use the short form for names defined in the same (or in an enclosing) namespace:
#include "Sales_data.h"
namespace cplusplus_primer { // reopen cplusplus_primer
// members defined inside the namespace may use unqualified names
std::istream&
operator>>(std::istream& in, Sales_data& s) { /* ... */}
}
It is also possible to define a namespace member outside its namespace definition. The namespace declaration of the name must be in scope, and the definition must specify the namespace to which the name belongs:
// namespace members defined outside the namespace must use qualified names
cplusplus_primer::Sales_data
cplusplus_primer::operator+(const Sales_data& lhs,
const Sales_data& rhs)
{
Sales_data ret(lhs);
// ...
}
As with class members defined outside a class, once the fully qualified name is seen, we are in the scope of the namespace. Inside the cplusplus_primer
namespace, we can use other namespace member names without qualification. Thus, even though Sales_data
is a member of the cplusplus_primer
namespace, we can use its unqualified name to define the parameters in this function.
Although a namespace member can be defined outside its namespace, such definitions must appear in an enclosing namespace. That is, we can define the Sales_data operator+
inside the cplusplus_primer
namespace or at global scope. We cannot define this operator in an unrelated namespace.
Template specializations must be defined in the same namespace that contains the original template (§ 16.5, p. 709). As with any other namespace name, so long as we have declared the specialization inside the namespace, we can define it outside the namespace:
// we must declare the specialization as a member of std
namespace std {
template <> struct hash<Sales_data>;
}
// having added the declaration for the specialization to std
// we can define the specialization outside the std namespace
template <> struct std::hash<Sales_data>
{
size_t operator()(const Sales_data& s) const
{ return hash<string>()(s.bookNo) ^
hash<unsigned>()(s.units_sold) ^
hash<double>()(s.revenue); }
// other members as before
};
Names defined at global scope (i.e., names declared outside any class, function, or namespace) are defined inside the global namespace. The global namespace is implicitly declared and exists in every program. Each file that defines entities at global scope (implicitly) adds those names to the global namespace.
The scope operator can be used to refer to members of the global namespace. Because the global namespace is implicit, it does not have a name; the notation
::member_name
refers to a member of the global namespace.
A nested namespace is a namespace defined inside another namespace:
namespace cplusplus_primer {
// first nested namespace: defines the Query portion of the library
namespace QueryLib {
class Query { /* ... */ };
Query operator&(const Query&, const Query&);
// ...
}
// second nested namespace: defines the Sales_data portion of the library
namespace Bookstore {
class Quote { /* ... */ };
class Disc_quote : public Quote { /* ... */ };
// ...
}
}
The cplusplus_primer
namespace now contains two nested namespaces: the namespaces named QueryLib
and Bookstore
.
A nested namespace is a nested scope—its scope is nested within the namespace that contains it. Nested namespace names follow the normal rules: Names declared in an inner namespace hide declarations of the same name in an outer namespace. Names defined inside a nested namespace are local to that inner namespace. Code in the outer parts of the enclosing namespace may refer to a name in a nested namespace only through its qualified name: For example, the name of the class declared in the nested namespace QueryLib
is
cplusplus_primer::QueryLib::Query
The new standard introduced a new kind of nested namespace, an inline namespace. Unlike ordinary nested namespaces, names in an inline namespace can be used as if they were direct members of the enclosing namespace. That is, we need not qualify names from an inline namespace by their namespace name. We can access them using only the name of the enclosing namespace.
An inline namespace is defined by preceding the keyword namespace
with the keyword inline
:
inline namespace FifthEd {
// namespace for the code from the Primer Fifth Edition
}
namespace FifthEd { // implicitly inline
class Query_base { /* ... * /};
// other Query-related declarations
}
The keyword must appear on the first definition of the namespace. If the namespace is later reopened, the keyword inline
need not be, but may be, repeated.
Inline namespaces are often used when code changes from one release of an application to the next. For example, we can put all the code from the current edition of the Primer into an inline namespace. Code for previous versions would be in non-inlined namespaces:
namespace FourthEd {
class Item_base { /* ... */};
class Query_base { /* ... */};
// other code from the Fourth Edition
}
The overall cplusplus_primer
namespace would include the definitions of both namespaces. For example, assuming that each namespace was defined in a header with the corresponding name, we’d define cplusplus_primer
as follows:
namespace cplusplus_primer {
#include "FifthEd.h"
#include "FourthEd.h"
}
Because FifthEd
is inline, code that refers to cplusplus_primer::
will get the version from that namespace. If we want the earlier edition code, we can access it as we would any other nested namespace, by using the names of all the enclosing namespaces: for example, cplusplus_primer::FourthEd::Query_base
.
An unnamed namespace is the keyword namespace
followed immediately by a block of declarations delimited by curly braces. Variables defined in an unnamed namespace have static lifetime: They are created before their first use and destroyed when the program ends.
An unnamed namespace may be discontiguous within a given file but does not span files. Each file has its own unnamed namespace. If two files contain unnamed namespaces, those namespaces are unrelated. Both unnamed namespaces can define the same name; those definitions would refer to different entities. If a header defines an unnamed namespace, the names in that namespace define different entities local to each file that includes the header.
Unlike other namespaces, an unnamed namespace is local to a particular file and never spans multiple files.
Names defined in an unnamed namespace are used directly; after all, there is no namespace name with which to qualify them. It is not possible to use the scope operator to refer to members of unnamed namespaces.
Names defined in an unnamed namespace are in the same scope as the scope at which the namespace is defined. If an unnamed namespace is defined at the outermost scope in the file, then names in the unnamed namespace must differ from names defined at global scope:
int i; // global declaration for i
namespace {
int i;
}
// ambiguous: defined globally and in an unnested, unnamed namespace
i = 10;
In all other ways, the members of an unnamed namespace are normal program entities. An unnamed namespace, like any other namespace, may be nested inside another namespace. If the unnamed namespace is nested, then names in it are accessed in the normal way, using the enclosing namespace name(s):
namespace local {
namespace {
int i;
}
}
// ok: i defined in a nested unnamed namespace is distinct from global i
local::i = 42;
Prior to the introduction of namespaces, programs declared names as
static
to make them local to a file. The use of file statics is inherited from C. In C, a global entity declaredstatic
is invisible outside the file in which it is declared.The use of file
static
declarations is deprecated by the C++ standard. File statics should be avoided and unnamed namespaces used instead.
Exercises Section 18.2.1
Exercise 18.12: Organize the programs you have written to answer the questions in each chapter into their own namespaces. That is, namespace
chapter15
would contain code for theQuery
programs andchapter10
would contain theTextQuery
code. Using this structure, compile theQuery
code examples.Exercise 18.14: Suppose we have the following declaration of the
operator*
that is a member of the nested namespacemathLib::MatrixLib
:namespace mathLib {
namespace MatrixLib {
class matrix { /* ... */ };
matrix operator*
(const matrix &, const matrix &);
// ...
}
}How would you declare this operator in global scope?
Referring to namespace members as namespace_name::member_name
is admittedly cumbersome, especially if the namespace name is long. Fortunately, there are ways to make it easier to use namespace members. Our programs have used one of these ways, using
declarations (§ 3.1, p. 82). The others, namespace aliases and using
directives, will be described in this section.
A namespace alias can be used to associate a shorter synonym with a namespace name. For example, a long namespace name such as
namespace cplusplus_primer { /* ... */ };
can be associated with a shorter synonym as follows:
namespace primer = cplusplus_primer;
A namespace alias declaration begins with the keyword namespace
, followed by the alias name, followed by the =
sign, followed by the original namespace name and a semicolon. It is an error if the original namespace name has not already been defined as a namespace.
A namespace alias can also refer to a nested namespace:
namespace Qlib = cplusplus_primer::QueryLib;
Qlib::Query q;
A namespace can have many synonyms, or aliases. All the aliases and the original namespace name can be used interchangeably.
using
Declarations: A RecapA using
declaration introduces only one namespace member at a time. It allows us to be very specific regarding which names are used in our programs.
Names introduced in a using
declaration obey normal scope rules: They are visible from the point of the using
declaration to the end of the scope in which the declaration appears. Entities with the same name defined in an outer scope are hidden. The unqualified name may be used only within the scope in which it is declared and in scopes nested within that scope. Once the scope ends, the fully qualified name must be used.
A using
declaration can appear in global, local, namespace, or class scope. In class scope, such declarations may only refer to a base class member (§ 15.5, p. 615).
using
DirectivesA using
directive, like a using
declaration, allows us to use the unqualified form of a namespace name. Unlike a using
declaration, we retain no control over which names are made visible—they all are.
A using
directive begins with the keyword using
, followed by the keyword namespace
, followed by a namespace name. It is an error if the name is not a previously defined namespace name. A using
directive may appear in global, local, or namespace scope. It may not appear in a class scope.
These directives make all the names from a specific namespace visible without qualification. The short form names can be used from the point of the using
directive to the end of the scope in which the using
directive appears.
Providing a
using
directive for namespaces, such asstd
, that our application does not control reintroduces all the name collision problems inherent in using multiple libraries.
using
Directives and ScopeThe scope of names introduced by a using
directive is more complicated than the scope of names in using
declarations. As we’ve seen, a using
declaration puts the name in the same scope as that of the using
declaration itself. It is as if the using
declaration declares a local alias for the namespace member.
A using
directive does not declare local aliases. Rather, it has the effect of lifting the namespace members into the nearest scope that contains both the namespace itself and the using
directive.
This difference in scope between a using
declaration and a using
directive stems directly from how these two facilities work. In the case of a using
declaration, we are simply making name directly accessible in the local scope. In contrast, a using
directive makes the entire contents of a namespace available In general, a namespace might include definitions that cannot appear in a local scope. As a consequence, a using
directive is treated as if it appeared in the nearest enclosing namespace scope.
In the simplest case, assume we have a namespace A
and a function f
, both defined at global scope. If f
has a using
directive for A
, then in f
it will be as if the names in A
appeared in the global scope prior to the definition of f
:
// namespace A and function f are defined at global scope
namespace A {
int i, j;
}
void f()
{
using namespace A; // injects the names from A into the global scope
cout << i * j << endl; // uses i and j from namespace A
// ...
}
using
Directives ExampleLet’s look at an example:
namespace blip {
int i = 16, j = 15, k = 23;
// other declarations
}
int j = 0; // ok: j inside blip is hidden inside a namespace
void manip()
{
// using directive; the names in blip are ''added'' to the global scope
using namespace blip; // clash between ::j and blip::j
// detected only if j is used
++i; // sets blip::i to 17
++j; // error ambiguous: global j or blip::j?
++::j; // ok: sets global j to 1
++blip::j; // ok: sets blip::j to 16
int k = 97; // local k hides blip::k
++k; // sets local k to 98
}
The using
directive in manip
makes all the names in blip
directly accessible; code inside manip
can refer to the names of these members, using their short form.
The members of blip
appear as if they were defined in the scope in which both blip
and manip
are defined. Assuming manip
is defined at global scope, then the members of blip
appear as if they were declared in global scope.
When a namespace is injected into an enclosing scope, it is possible for names in the namespace to conflict with other names defined in that (enclosing) scope. For example, inside manip
, the blip
member j
conflicts with the global object named j
. Such conflicts are permitted, but to use the name, we must explicitly indicate which version is wanted. Any unqualified use of j
within manip
is ambiguous.
To use a name such as j
, we must use the scope operator to indicate which name is wanted. We would write ::j
to obtain the variable defined in global scope. To use the j
defined in blip
, we must use its qualified name, blip::j
.
Because the names are in different scopes, local declarations within manip
may hide some of the namespace member names. The local variable k
hides the namespace member blip::k
. Referring to k
within manip
is not ambiguous; it refers to the local variable k
.
using
Declarations or DirectivesA header that has a using
directive or declaration at its top-level scope injects names into every file that includes the header. Ordinarily, headers should define only the names that are part of its interface, not names used in its own implementation. As a result, header files should not contain using
directives or using
declarations except inside functions or namespaces (§ 3.1, p. 83).
using
directives, which inject all the names from a namespace, are deceptively simple to use: With only a single statement, all the member names of a namespace are suddenly visible. Although this approach may seem simple, it can introduce its own problems. If an application uses many libraries, and if the names within these libraries are made visible withusing
directives, then we are back to square one, and the global namespace pollution problem reappears.Moreover, it is possible that a working program will fail to compile when a new version of the library is introduced. This problem can arise if a new version introduces a name that conflicts with a name that the application is using.
Another problem is that ambiguity errors caused by
using
directives are detected only at the point of use. This late detection means that conflicts can arise long after introducing a particular library. If the program begins using a new part of the library, previously undetected collisions may arise.Rather than relying on a
using
directive, it is better to use ausing
declaration for each namespace name used in the program. Doing so reduces the number of names injected into the namespace. Ambiguity errors caused byusing
declarations are detected at the point of declaration, not use, and so are easier to find and fix.One place where
using
directives are useful is in the implementation files of the namespace itself.
Exercises Section 18.2.2
Exercise 18.15: Explain the differences between
using
declarations and directives.Exercise 18.16: Explain the following code assuming
using
declarations for all the members of namespaceExercise
are located at the location labeled position 1. What if they appear at position 2 instead? Now answer the same question but replace theusing
declarations with ausing
directive for namespaceExercise
.namespace Exercise {
int ivar = 0;
double dvar = 0;
const int limit = 1000;
}
int ivar = 0;
// position 1
void manip() {
// position 2
double dvar = 3.1416;
int iobj = limit + 1;
++ivar;
++::ivar;
}Exercise 18.17: Write code to test your answers to the previous question.
Name lookup for names used inside a namespace follows the normal lookup rules: The search looks outward through the enclosing scopes. An enclosing scope might be one or more nested namespaces, ending in the all-encompassing global namespace. Only names that have been declared before the point of use that are in blocks that are still open are considered:
namespace A {
int i;
namespace B {
int i; // hides A::i within B
int j;
int f1()
{
int j; // j is local to f1 and hides A::B::j
return i; // returns B::i
}
} // namespace B is closed and names in it are no longer visible
int f2() {
return j; // error: j is not defined
}
int j = i; // initialized from A::i
}
When a class is wrapped in a namespace, the normal lookup still happens: When a name is used by a member function, look for that name in the member first, then within the class (including base classes), then look in the enclosing scopes, one or more of which might be a namespace:
namespace A {
int i;
int k;
class C1 {
public:
C1(): i(0), j(0) { } // ok: initializes C1::i and C1::j
int f1() { return k; } // returns A::k
int f2() { return h; } // error: h is not defined
int f3();
private:
int i; // hides A::i within C1
int j;
};
int h = i; // initialized from A::i
}
// member f3 is defined outside class C1 and outside namespace A
int A::C1::f3() { return h; } // ok: returns A::h
With the exception of member function definitions that appear inside the class body (§ 7.4.1, p. 283), scopes are always searched upward; names must be declared before they can be used. Hence, the return
in f2
will not compile. It attempts to reference the name h
from namespace A
, but h
has not yet been defined. Had that name been defined in A
before the definition of C1
, the use of h
would be legal. Similarly, the use of h
inside f3
is okay, because f3
is defined after A::h
.
The order in which scopes are examined to find a name can be inferred from the qualified name of a function. The qualified name indicates, in reverse order, the scopes that are searched.
The qualifiers A::C1::f3
indicate the reverse order in which the class scopes and namespace scopes are to be searched. The first scope searched is that of the function f3
. Then the class scope of its enclosing class C1
is searched. The scope of the namespace A
is searched last before the scope containing the definition of f3
is examined.
Consider the following simple program:
std::string s;
std::cin >> s;
As we know, this call is equivalent to (§ 14.1, p. 553):
operator>>(std::cin, s);
This operator>>
function is defined by the string
library, which in turn is defined in the std
namespace. Yet we can we call operator>>
without an std::
qualifier and without a using
declaration.
We can directly access the output operator because there is an important exception to the rule that names defined in a namespace are hidden. When we pass an object of a class type to a function, the compiler searches the namespace in which the argument’s class is defined in addition to the normal scope lookup. This exception also applies for calls that pass pointers or references to a class type.
In this example, when the compiler sees the “call” to operator>>
, it looks for a matching function in the current scope, including the scopes enclosing the output statement. In addition, because the >>
expression has parameters of class type, the compiler also looks in the namespace(s) in which the types of cin
and s
are defined. Thus, for this call, the compiler looks in the std
namespace, which defines the istream
and string
types. When it searches std
, the compiler finds the string
output operator function.
This exception in the lookup rules allows nonmember functions that are conceptually part of the interface to a class to be used without requiring a separate using
declaration. In the absence of this exception to the lookup rules, either we would have to provide an appropriate using
declaration for the output operator:
using std::operator>>; // needed to allow cin >> s
or we would have to use the function-call notation in order to include the namespace qualifer:
std::operator>>(std::cin, s); // ok: explicitly use std::>>
There would be no way to use operator syntax. Either of these declarations is awkward and would make simple uses of the IO library more complicated.
std::move
and std::forward
Many, perhaps even most, C++ programmers never have to think about argument-dependent lookup. Ordinarily, if an application defines a name that is also defined in the library, one of two things is true: Either normal overloading determines (correctly) whether a particular call is intended for the application version or the one from the library, or the application never intends to use the library function.
Now consider the library move
and forward
functions. Both of these functions are template functions, and the library defines versions of them that have a single rvalue reference function parameter. As we’ve seen, in a function template, an rvalue reference parameter can match any type (§ 16.2.6, p. 690). If our application defines a function named move
that takes a single parameter, then—no matter what type the parameter has—the application’s version of move
will collide with the library version. Similarly for forward
.
As a result, name collisions with move
(and forward
) are more likely than collisions with other library functions. In addition, because move
and forward
do very specialized type manipulations, the chances that an application specifically wants to override the behavior of these functions are pretty small.
The fact that collisions are more likely—and are less likely to be intentional—explains why we suggest always using the fully qualified versions of these names (§ 12.1.5, p. 470). So long as we write std::move
rather than move
, we know that we will get the version from the standard library.
Recall that when a class declares a friend, the friend declaration does not make the friend visible (§ 7.2.1, p. 270). However, an otherwise undeclared class or function that is first named in a friend declaration is assumed to be a member of the closest enclosing namespace. The combination of this rule and argument-dependent lookup can lead to surprises:
namespace A {
class C {
// two friends; neither is declared apart from a friend declaration
// these functions implicitly are members of namespace A
friend void f2(); // won't be found, unless otherwise declared
friend void f(const C&); // found by argument-dependent lookup
};
}
Here, both f
and f2
are members of namespace A
. Through argument-dependent lookup, we can call f
even if there is no additional declaration for f
:
int main()
{
A::C cobj;
f(cobj); // ok: finds A::f through the friend declaration in A::C
f2(); // error: A::f2 not declared
}
Because f
takes an argument of a class type, and f
is implicitly declared in the same namespace as C
, f
is found when called. Because f2
has no parameter, it will not be found.
Exercises Section 18.2.3
Exercise 18.18: Given the following typical definition of
swap
§ 13.3 (p. 517), determine which version ofswap
is used ifmem1
is astring
. What ifmem1
is anint
? Explain how name lookup works in both cases.void swap(T v1, T v2)
{
using std::swap;
swap(v1.mem1, v2.mem1);
// swap remaining members of type T
}Exercise 18.19: What if the call to
swap
wasstd::swap(v1.mem1, v2.mem1)?
Namespaces have two impacts on function matching (§ 6.4, p. 233). One of these should be obvious: A using
declaration or directive can add functions to the candidate set. The other is much more subtle.
As we saw in the previous section, name lookup for functions that have class-type arguments includes the namespace in which each argument’s class is defined. This rule also impacts how we determine the candidate set. Each namespace that defines a class used as an argument (and those that define its base classes) is searched for candidate functions. Any functions in those namespaces that have the same name as the called function are added to the candidate set. These functions are added even though they otherwise are not visible at the point of the call:
namespace NS {
class Quote { /* ... */ };
void display(const Quote&) { /* ... */ }
}
// Bulk_item's base class is declared in namespace NS
class Bulk_item : public NS::Quote { /* ... */ };
int main() {
Bulk_item book1;
display(book1);
return 0;
}
The argument we passed to display
has class type Bulk_item
. The candidate functions for the call to display
are not only the functions with declarations that are in scope where display
is called, but also the functions in the namespace where Bulk_item
and its base class, Quote
, are declared. The function display(const Quote&)
declared in namespace NS
is added to the set of candidate functions.
using
DeclarationsTo understand the interaction between using
declarations and overloading, it is important to remember that a using
declaration declares a name, not a specific function (§ 15.6, p. 621):
using NS::print(int); // error: cannot specify a parameter list
using NS::print; // ok: using declarations specify names only
When we write a using
declaration for a function, all the versions of that function are brought into the current scope.
A using
declaration incorporates all versions to ensure that the interface of the namespace is not violated. The author of a library provided different functions for a reason. Allowing users to selectively ignore some but not all of the functions from a set of overloaded functions could lead to surprising program behavior.
The functions introduced by a using
declaration overload any other declarations of the functions with the same name already present in the scope where the using
declaration appears. If the using
declaration appears in a local scope, these names hide existing declarations for that name in the outer scope. If the using
declaration introduces a function in a scope that already has a function of the same name with the same parameter list, then the using
declaration is in error. Otherwise, the using
declaration defines additional overloaded instances of the given name. The effect is to increase the set of candidate functions.
using
DirectivesA using
directive lifts the namespace members into the enclosing scope. If a namespace function has the same name as a function declared in the scope at which the namespace is placed, then the namespace member is added to the overload set:
namespace libs_R_us {
extern void print(int);
extern void print(double);
}
// ordinary declaration
void print(const std::string &);
// this using directive adds names to the candidate set for calls to print:
using namespace libs_R_us;
// the candidates for calls to print at this point in the program are:
// print(int) from libs_R_us
// print(double) from libs_R_us
// print(const std::string &) declared explicitly
void fooBar(int ival)
{
print("Value: "); // calls global print(const string &)
print(ival); // calls libs_R_us::print(int)
}
Differently from how using
declarations work, it is not an error if a using
directive introduces a function that has the same parameters as an existing function. As with other conflicts generated by using
directives, there is no problem unless we try to call the function without specifying whether we want the one from the namespace or from the current scope.
using
DirectivesIf many using
directives are present, then the names from each namespace become part of the candidate set:
namespace AW {
int print(int);
}
namespace Primer {
double print(double);
}
// using directives create an overload set of functions from different namespaces
using namespace AW;
using namespace Primer;
long double print(long double);
int main() {
print(1); // calls AW::print(int)
print(3.1); // calls Primer::print(double)
return 0;
}
The overload set for the function print
in global scope contains the functions print(int), print(double)
, and print(long double)
. These functions are all part of the overload set considered for the function calls in main
, even though these functions were originally declared in different namespace scopes.
Exercises Section 18.2.4
Exercise 18.20: In the following code, determine which function, if any, matches the call to
compute
. List the candidate and viable functions. What type conversions, if any, are applied to the argument to match the parameter in each viable function?namespace primerLib {
void compute();
void compute(const void *);
}
using primerLib::compute;
void compute(int);
void compute(double, double = 3.4);
void compute(char*, char* = 0);
void f()
{
compute(0);
}What would happen if the
using
declaration were located inmain
before the call tocompute?
Answer the same questions as before.