DekGenius.com
Team LiB   Previous Section   Next Section

7.7 Instantiation

Template declarations and specializations describe the form that a class or function can take but do not create any actual classes or functions. To do these things, you must instantiate a template.

Most often, you implicitly instantiate a template by using it in a function call, object declaration, or similar context. The template instance requires a template argument for each template parameter. The arguments can be explicit (enclosed in angle brackets and separated by commas) or implicit (default arguments for class templates or deduced arguments for function templates):

template<typename T> T plus(T a, T b) { return a + b; }
template<typename T = int> struct wrapper {
  T value;
};
int x = plus(10, 20); // Instantiate plus<int>.
wrapper<double> x;    // Instantiate wrapper<double>.
wrapper w;            // Instantiate wrapper<int>.

A class member expression (. or -> operator) that names an instance of a member function template with template arguments must use the template keyword before the member name. Similarly, a qualified name of a member template must use template before the member name. Otherwise, the < symbol that introduces the template argument list is interpreted as the less-than operator:

class bigfloat {
  template<unsigned N> double round(  );
  template<unsigned N> static double round(double);
};

bigfloat f;
std::cout << f.template round<2>(  ) << '\n';
std::cout << bigfloat::template round<8>(PI) << '\n';

figs/acorn.gif

Instantiating a class template does not necessarily instantiate all the members. Members of a class template are instantiated only when they are needed. A static data member is "needed" if the program refers to the member. A member function is "needed" if the function is called, has its address taken, or participates in overload resolution. An implementation is permitted to define every virtual function in a class template, and most implementations do. Each instance of a class template has its own, separate copy of the class's static data members.

If a class template has any specializations, the compiler must choose which specialization to instantiate. It starts by choosing which partial specializations match the template arguments. A partial specialization matches if the specialized template arguments can be deduced from the actual template arguments. See Section 7.3.2 earlier in this chapter.

If no specializations match, the compiler instantiates the primary template. If one specialization matches, that one is instantiated. Otherwise, the best match is chosen, in which "best" means most specific. An error results if two or more specializations are tied for best. Template specialization A is at least as specific as template specialization B if the parameters of A can be deduced from B's template parameter list. No implicit conversion takes place when comparing specializations. The following example shows several instances of the demo class template. The template instantiations are instances of the primary template or one of the three partial specializations:

template<typename T, typename U, int N> class demo {};
template<typename T, int N> class demo<T, int, N> {};
template<typename T> class demo<T, T, 0> {};
template<typename T> class demo<T*, T, 1> {};
demo<int,  char,  1> w;     // Primary template
demo<char, int,  10> x;     // First specialization
demo<char, char,  0> y;     // Second specialization
demo<char, char,  1> z;     // Primary template
demo<char*,char,  1> p;     // Third template
demo<char*,char,  0> q;     // Primary template
demo<char*,char*, 0> r;     // Second template
demo<int*, int,   1> s;     // Error: ambiguous

In addition to the obvious points at which a function or class is needed (e.g., calling the function, taking the address of the function, declaring an object whose type is the class, or casting an object to the class), there are more subtle points of instantiation. For example, a template can be instantiated if it is used in the value of a default function argument, and the default argument is needed in a function call.

figs/acorn.gif

A function template is needed if an instance of the template is needed for overload resolution. If a class template participates in function overloading (that is, as part of a function parameter type), but the template does not need to be instantiated to resolve the overload (e.g., because the function parameter is a pointer or reference to the class template), the class template may or may not be instantiated. The standard leaves this up to the implementation.

If a template declaration contains an error, the compiler might diagnose the error when it compiles the template or when it instantiates the template. If a program does not instantiate the template, you might find that you can compile a program successfully using one compiler (which reports errors only when a template is instantiated), but not a different compiler (which reports errors when a template is declared).

You can explicitly instantiate a template. Use a bare template keyword followed by a declaration that supplies the template arguments for the template:

template long int plus<long int>(long int a, long int b);
template short plus(short a, short b); // Deduce plus<short>.
template struct wrapper<unsigned const char*>;

The class, function, or member template must be defined before it can be instantiated. If you are instantiating a specialization, the template specialization must appear before the instantiation in the source. The behavior is undefined if a template is specialized after it has been instantiated for the same template arguments. Example 7-10 illustrates template instantiation.

Example 7-10. Instantiating templates
#include <iomanip>
#include <iostream>
#include <ostream>
const double pi = 3.1415926535897932;
// Function template
template<typename T> T sqr(T x)
{
  return x * x;
}

// Class template
template<typename T>
class circle
{
public:
  circle(T r) : radius_(r) {}
  // sqr<> is instantiated when circle<> is instantiated.
  T area(  ) const { return pi * sqr(radius_); }
  T radius(  ) const { return radius_; }
private:
  T radius_;
};

// Function template
template<typename T>
void print(T obj)
{
  std::cout << obj << '\n';
}

// Overload the function template with another template.
template<typename T>
void print(const circle<T>& c)
{
  std::cout << "circle(" << c.radius(  ) << ")\n";
}

template int sqr<int>(int); // Explicit instantiation

// Explicit instantiation of circle<double> and implicit instantiation of
// sqr<double>
template class circle<double>;

// Error: after instantiation of sqr<double>, illegal to specialize it
template<> double sqr(double x)
{
  return x * x;
}

int main(  )
{
  using namespace std;
  // No circle<> instance is needed yet, even to resolve overloaded print
  // function.
  print(42);
  for (int i = 0; i < 10; ++i)
    // Implicit instantiation of sqr<int>
    cout << setw(2) << i << setw(4) << sqr(i) << '\n';
  // Instantiation of circle<float> and therefore sqr<float>
  circle<float> unit(1.0f);
  // Implicit instantiation of print<circle<float> >
  print(unit);
}
    Team LiB   Previous Section   Next Section