You can always use such explicit function template specification as in the example above, but it is often convenient to leave off the template arguments and let the compiler deduce them from the function arguments, like this:.
int z = min(i, j);
If both i and j are ints, the compiler knows that you need min
int z = min(x, j); // x is a double
Since x and j are distinct types, no single parameter matches the template parameter T in the definition of min; so the call does not match the template declaration. You can work around this difficulty by casting one argument to the other’s type or by reverting to the fully-specified call syntax, as in:.
int z = min
This tells the compiler to generate the double version of min, after which j can be promoted to a double by normal standard conversion rules (because the function min
You might be tempted to require two parameters for min, allowing the types of the arguments to be independent, like this:
template
const T& min(const T& a, const U& b) {
return (a < b) ? a : b;
}
This is often a good strategy, but in this case it is problematic because min must return a value, and there is no satisfactory way to determine which type it should be (T or U?).
If the return type of a function template is an independent template parameter, you must always specify its type explicitly when you call it, since there is no argument from which to deduce it. Such is the case with the fromString template below.
//: C05:StringConv.h
#ifndef STRINGCONV_H
#define STRINGCONV_H
// Function templates to convert to and from strings
#include
#include
template
T fromString(const std::string& s) {
std::istringstream is(s);
T t;
is >> t;
return t;
}
template
std::string toString(const T& t) {
std::ostringstream s;
s << t;
return s.str();
}
#endif // STRINGCONV_H ///:~
These function templates provide conversions to and from std:: string for any types that provide a stream inserter or extractor, respectively. Here’s a test program that includes the use of the standard library complex number type:.
//: C05:StringConvTest.cpp
#include "StringConv.h"
#include
#include
using namespace std;
int main() {
int i = 1234;
cout << "i == \"" << toString(i) << "\"\n";
float x = 567.89;
cout << "x == \"" << toString(x) << "\"\n";
complex
cout << "c == \"" << toString(c) << "\"\n";
cout << endl;
i = fromString
cout << "i == " << i << endl;
x = fromString
cout << "x == " << x << endl;
c = fromString< complex
cout << "c == " << c << endl;
} ///:~
The output is what you’d expect:.
i == "1234"
x == "567.89"
c == "(1,2)"
i == 1234
x == 567.89
c == (1,2)
Notice that in each of the instantiations of fromString, the template parameter is specified in the call. If you have a function template with template parameters for the parameter types as well as the return types, it is important to declare the return type parameter first; otherwise you won’t be able to omit the type parameters for the function parameters. As an illustration, consider the following well-known function template:[51]
//: C05:ImplicitCast.cpp
template
R implicit_cast(const P& p) {
return p;
}
int main() {
int i = 1;
float x = implicit_cast
int j = implicit_cast
// char* p = implicit_cast
} ///:~
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
using std::size_t;
template
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
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
} ///:~