Team LiB   Previous Section   Next Section

7.1 Overview of Templates

A template declaration can be a function declaration, function definition, class declaration, or class definition. The template declaration takes one or more parameters, which can be values, types, or class templates.

In most cases, you can use a template simply by naming the template and providing arguments for the template parameters: constant expressions, types, or template references. This is known as instantiating the template. You can instantiate a template at its point of use, or declare a separate instantiation as a class or function declaration. An instance of a function template creates a function; an instance of a class template creates a class and all of its members.

A template lets you define a class or function once for a wide range of template arguments, but sometimes you need to customize a template for particular arguments. This is known as specializing the template. A template specialization, as its name implies, is a special case of the template pattern. When you instantiate a template, the compiler uses the template arguments to pick a specialization, or if no specialization matches the arguments, the original template declaration. A specialization can be total or partial. A total specialization specifies values for all of the template arguments, and a partial specialization specifies only some of the template arguments.

The terminology used in this book reflects the terminology that many C++ programmers have adopted, even though that terminology differs slightly from that used in the C++ standard. In the standard, "specialization" means an instance of a template. "Instantiation" also refers to an instance of a template. When you declare a special case of a template, that is known as explicit specialization.

Many C++ programmers prefer to keep specialization and instantiation as separate concepts and separate terms, and I have adopted the simpler terminology for this book.

Note that a template declaration defines only the pattern. A specialization defines a pattern that applies to a specific set of template arguments. Only by instantiating a template do you declare or define a function or class. When you instantiate a template, the compiler uses the template arguments to pick which pattern to instantiate: a specialization or the main template.

Writing a template is more difficult than writing a non-template class or function. The template can be instantiated in almost any context, and the context can affect how the template definition is interpreted. Name lookup rules are more complicated for templates than for non-templates.

Example 7-1 shows several different kinds of templates and their uses.

Example 7-1. Declaring and using templates
#include <cmath>
#include <complex>
#include <iostream>
#include <ostream>

// Template declaration of point
template<typename T>
class point {
public:
  typedef T value_type;
  point(const T& x, const T& y) : x_(x), y_(y) {}
  point() : x_(T(  )), y_(T(  )) {}
  T x(  )            const { return x_; }
  T& x(  )                 { return x_; }
  void x(const T& new_x)   { x_ = new_x; }
  T y(  )            const { return y_; }
  T& y(  )                 { return y_; }
  void y(const T& new_y)   { y_ = new_y; }
private:
  T x_, y_;
};

// Instantiate point<>.
typedef point<std::complex<double> > strange;
strange s(std::complex<double>(1, 2),
          std::complex<double>(3, 4));

// Specialize point<int> to use call-by-value instead of const references.
template<>
class point<int> {
public:
  typedef int value_type;
  point(int x, int y) : x_(x), y_(y) {}
  point(  ) : x_(0), y_(0) {}
  int x(  )     const { return x_; }
  int& x(  )          { return x_; }
  void x(int new_x)   { x_ = new_x; }
  int y(  )     const { return y_; }
  int& y(  )          { return y_; }
  void y(int new_y)   { y_ = new_y; }
private:
  int x_, y_;
};

// Instance of the specialized point<int>
point<int> p(42, 0);
// Instance of the general point<>, using long as the template argument
point<long> p(42, 0);

// Function template
template<typename T>
T abs(T x)
{
  return x < 0 ? -x : x;
}

namespace {
  // Explicit instantiation
  const int abs_char_min1 = abs<int>(CHAR_MIN);
  // Implicit instantiation
  const int abs_char_min2 = abs(CHAR_MIN);
}

// Overload abs(  ) with another function template.
template<typename floatT, typename T>
floatT abs(const point<T>& p)
{
  return std::sqrt(static_cast<floatT>(p.x(  )*p.x(  ) +
                                       p.y(  )*p.y(  )));
}

int main(  )
{
  point<double> p;
  // Call instance of function template. Compiler deduces second template
  // argument (double) from the type of p.
  double x = abs<long double>(p);
  std::cout << x << '\n'; // prints 0
  std::cout << abs_char_min1 << '\n';
}
    Team LiB   Previous Section   Next Section