Writing Overloaded Templates

As an example, we’ll build a set of functions that might be useful during debugging. We’ll name our debugging functions debug_rep, each of which will return a string representation of a given object. We’ll start by writing the most general version of this function as a template that takes a reference to a const object:

// print any type we don't otherwise handle
template <typename T> string debug_rep(const T &t)
{
    ostringstream ret; // see § 8.3 (p. 321)
    ret << t; // uses T's output operator to print a representation of t
    return ret.str(); // return a copy of the string to which ret is bound
}

This function can be used to generate a string corresponding to an object of any type that has an output operator.

Next, we’ll define a version of debug_rep to print pointers:

// print pointers as their pointer value, followed by the object to which the pointer points
// NB: this function will not work properly with char*; see § 16.3 (p. 698)
template <typename T> string debug_rep(T *p)
{
    ostringstream ret;
    ret << "pointer: " << p;         // print the pointer's own value
    if (p)
        ret << " " << debug_rep(*p); // print the value to which p points
    else
        ret << " null pointer";      // or indicate that the p is null
    return ret.str(); // return a copy of the string to which ret is bound
}

This version generates a string that contains the pointer’s own value and calls debug_rep to print the object to which that pointer points. Note that this function can’t be used to print character pointers, because the IO library defines a version of the << for char* values. That version of << assumes the pointer denotes a null-terminated character array, and prints the contents of the array, not its address. We’ll see in § 16.3 (p. 698) how to handle character pointers.

We might use these functions as follows:

string s("hi");
cout << debug_rep(s) << endl;

For this call, only the first version of debug_rep is viable. The second version of debug_rep requires a pointer parameter, and in this call we passed a nonpointer object. There is no way to instantiate a function template that expects a pointer type from a nonpointer argument, so argument deduction fails. Because there is only one viable function, that is the one that is called.

If we call debug_rep with a pointer:

cout << debug_rep(&s) << endl;

both functions generate viable instantiations:

debug_rep(const string* &), which is the instantiation of the first version of debug_rep with T bound to string*

debug_rep(string*), which is the instantiation of the second version of debug_rep with T bound to string

The instantiation of the second version of debug_rep is an exact match for this call. The instantiation of the first version requires a conversion of the plain pointer to a pointer to const. Normal function matching says we should prefer the second template, and indeed that is the one that is run.

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

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