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.