Follow Techotopia on Twitter

On-line Guides
All Guides
eBook Store
iOS / Android
Linux for Beginners
Office Productivity
Linux Installation
Linux Security
Linux Utilities
Linux Virtualization
Linux Kernel
System/Network Admin
Programming
Scripting Languages
Development Tools
Web Development
GUI Toolkits/Desktop
Databases
Mail Systems
openSolaris
Eclipse Documentation
Techotopia.com
Virtuatopia.com
Answertopia.com

How To Guides
Virtualization
General System Admin
Linux Security
Linux Filesystems
Web Servers
Graphics & Desktop
PC Hardware
Windows
Problem Solutions
Privacy Policy

  




 

 

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

Type deduction of function template arguments

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>( ), which it then instantiates automatically. The types must be identical because the template was originally specified with only one template type argument used for both function parameters. No standard conversions are applied for function arguments whose type is specified by a template parameter. For example, if you wanted to find the minimum of an int and a double, the following attempt at a call to min( ) would fail:

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<double>(x, j);
 

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<double>(const double&, const double&) would then exist).

You might be tempted to require two parameters for min( ), allowing the types of the arguments to be independent, like this:

template<typename T, typename U>
const T& min(const T& a, const U& b) {
return (a < b) ? a : b;
}
 

This is often a good strategy, but here 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
// Function templates to convert to and from strings.
#ifndef STRINGCONV_H
#define STRINGCONV_H
#include <string>
#include <sstream>
 
template<typename T> T fromString(const std::string& s) {
std::istringstream is(s);
T t;
is >> t;
return t;
}
 
template<typename T> 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 <complex>
#include <iostream>
#include "StringConv.h"
using namespace std;
 
int main() {
int i = 1234;
cout << "i == \"" << toString(i) << "\"" << endl;
float x = 567.89;
cout << "x == \"" << toString(x) << "\"" << endl;
complex<float> c(1.0, 2.0);
cout << "c == \"" << toString(c) << "\"" << endl;
cout << endl;
 
i = fromString<int>(string("1234"));
cout << "i == " << i << endl;
x = fromString<float>(string("567.89"));
cout << "x == " << x << endl;
c = fromString<complex<float> >(string("(1.0,2.0)"));
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:[53]

//: 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 (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 conversions in your code that are allowed naturally.

With a little care you can even deduce array dimensions. This example has an array-initialization function template (init2) that performs such a deduction:

//: 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 be deduced.

Thinking in C++ Vol 2 - Practical Programming
Prev Home Next

 
 
   Reproduced courtesy of Bruce Eckel, MindView, Inc. Design by Interspire