18.2.4. Overloading and Namespaces

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.

Argument-Dependent Lookup and Overloading
Image

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.

Overloading and using Declarations

To 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.

Overloading and using Directives

A 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.

Overloading across Multiple using Directives

If 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 in main before the call to compute? Answer the same questions as before.


..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.37.12