Выбрать главу

Outer == int

Inner == bool

Full Inner == Outer<int>::Inner<bool>

The declaration of the variable inner in the main program instantiates both Inner<bool> and Outer<int>.

Member template functions cannot be declared virtual. Current compiler technology expects to be able to fix the size of a class’s virtual function table when the class is parsed. Allowing virtual member template functions would require knowing all calls to such member functions everywhere in the program ahead of time, which is not feasible, especially for multi-file projects.

Function template issues

Just as a class template describes a family of classes, a function template describes a family of functions. The syntax for creating either type of template is virtually identical, but they differ somewhat in how they are used. You must always use angle brackets when instantiating class templates and you must supply all non-default template arguments. With function templates, on the other hand, you can often omit the template arguments, and default template arguments are not even allowed.[ ]Consider a typical implementation of the min( ) function template declared in the <algorithm> header, which looks something like this:.

template<typename T>

const T& min(const T& a, const T& b) {

  return (a < b) ? a : b;

}

You could invoke this template by providing the type of the arguments in angle brackets, just like you do with class templates, as in:

int z = min<int>(i, j);

This syntax tells the compiler that a specialization of the min template is needed with int used in place of the parameter T, whereupon the compiler generates the corresponding code. Following the pattern of naming the classes generated from class templates, you can think of the name of the instantiated function as min<int>.

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 faiclass="underline" .

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 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 <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 "StringConv.h"

#include <iostream>

#include <complex>

using namespace std;

int main() {

  int i = 1234;

  cout << "i == \"" << toString(i) << "\"\n";

  float x = 567.89;

  cout << "x == \"" << toString(x) << "\"\n";

  complex<float> c(1.0, 2.0);

  cout << "c == \"" << toString(c) << "\"\n";

  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:[51]