//: C05:ImplicitCast.cpp
template<typename R, typename P>
R implicit_cast(const P& p) {
return p;
}
int main() {
int i = 1;
float x = implicit_cast<float>(i);
int j = implicit_cast<int>(x);
// char* p = implicit_cast<char*>(i);
} ///:~
If you interchange R and P in the template parameter list near the top of the file, it will be impossible to compile this program because the return type will remain unspecified (since the first template parameter would be the function’s parameter type). The last line (which is commented out) is illegal because there is no standard conversion from int to char*; implicit_cast is for revealing in your code conversions that are allowed naturally.
With a little care you can even deduce array dimensions. The following example has an array-initialization function template (init2) that does just that.
//: C05:ArraySize.cpp
#include <cstddef>
using std::size_t;
template<size_t R, size_t C, typename T>
void init1(T a[R][C]) {
for (size_t i = 0; i < R; ++i)
for (size_t j = 0; j < C; ++j)
a[i][j] = T();
}
template<size_t R, size_t C, class T>
void init2(T (&a)[R][C]) { // reference parameter
for (size_t i = 0; i < R; ++i)
for (size_t j = 0; j < C; ++j)
a[i][j] = T();
}
int main() {
int a[10][20];
init1<10,20>(a); // must specify
init2(a); // sizes deduced
} ///:~
Array dimensions are not passed as part of a function parameter’s type unless that parameter is passed by pointer or reference. The function template init2 declares a to be a reference to a two-dimensional array, so its dimensions R and C are deduced by the template facility, making init2 a handy way to initialize a two-dimensional array of any size. The template init1 does not pass the array by reference, so the sizes must be explicitly specified, although the type parameter can still deduced.
Function template overloading
As with functions, you can overload function templates that have the same name. When the compiler processes a function call in a program, it has to decide which template or ordinary function is the "best" fit for the call. Assuming the existence of the min function template introduced earlier, let’s add some ordinary functions to the mix:.
//: C05:MinTest.cpp
#include <cstring>
#include <iostream>
using std::strcmp;
using std::cout;
using std::endl;
template<typename T> const T& min(const T& a, const T& b) {
return (a < b) ? a : b;
}
const char* min(const char* a, const char* b) {
return (strcmp(a, b) < 0) ? a : b;
}
double min(double x, double y) {
return (x < y) ? x : y;
}
int main() {
const char *s2 = "say \"Ni-!\"", *s1 = "knights who";
cout << min(1, 2) << endl; // 1: 1 (template)
cout << min(1.0, 2.0) << endl; // 2: 1 (double)
cout << min(1, 2.0) << endl; // 3: 1 (double)
cout << min(s1, s2) << endl; // 4: knights who (const
// char*)
cout << min<>(s1, s2) << endl; // 5: say "Ni-!"
// (template)
} ///:~
In addition to the function template, this program defines two non-template functions: a C-style string version of min and a double version. If the template doesn’t exist at all, the call in line 1 above have invokes the double version of min because of the standard conversion from int to double. Since the template can generate an int version, however, that is considered a better match (of course!); so that’s what happens. The call in line 2 is an exact match for the double version, of course, and the call in line 3 also invokes the same function, implicitly converting 1 to 1.0. In line 4 the const char* version of min is called directly. In line 5 we force the compiler to use the template facility by appending empty angle brackets to the function name, whereupon it generates a const char* version from the template and uses it (which is verified by the wrong answer—it’s just comparing addresses![52]). If you’re wondering why we used using declarations in lieu of the using namespace std; directive, some compilers include headers behind the scenes that bring in std::min, which would conflict with our declarations of the name min.
As stated above, you can overload templates of the same name, as long as they can be distinguished by the compiler. You could, for example, declare a min function template that processes three arguments:
template<typename T>
const T& min(const T& a, const T& b, const T& c);
Versions of this template will be generated only for calls to min( ) that have three arguments of the same type.
Taking the address of a generated function template
In a number of situations you need to take the address of a function. For example, you may have a function that takes an argument of a pointer to another function. Of course, it’s possible that this other function might be generated from a template function, so you need some way to take that kind of address:[53]
//: C05:TemplateFunctionAddress.cpp
// Taking the address of a function generated
// from a template.
template <typename T> void f(T*) {}
void h(void (*pf)(int*)) {}
template <typename T>
void g(void (*pf)(T*)) {}
int main() {
// Full type specification:
h(&f<int>);
// Type deduction:
h(&f);
// Full type specification:
g<int>(&f<int>);
// Type deduction:
g(&f<int>);
// Partial (but sufficient) specification
g<int>(&f);
} ///:~
This example demonstrates a number of issues. First, even though you’re using templates, the signatures must match. The function h( ) takes a pointer to a function that takes an int* and returns void, and that’s what the template f produces. Second, the function that wants the function pointer as an argument can itself be a template, as in the case of the template g.