Templates

Templates

By Mike Toms

Overload, 1(1):, April 1993


Be honest, how often do you use templates? Never! Not very often! What are templates?

These are the answers that I usually get. All those with compilers prior to version 3.0 are forgiven as you do not have templates. As for those of you with V3.0, V3.1 shame on you. One of the oft-cited reasons for using C++ rather than C is the ease of building reusa­ble classes. These classes are, however, usually tied strongly to the datatypes for which they have been de­fined.

A template class, or template function for that matter, can be built in a relatively datatype-free manner, needing the type only when an instance of that class or function is required.

The definition of a template, in my little dictionary, is 'a pattern used to cut out shapes accurately'. This defini­tion will not do for a C++ template, but it is a start. A C++ template is a pattern from which functions or classes can be crafted.

Templates are used where several functions have common functionality, but differ in the kind of parameter(s) used. Take the example of a function that displays a value at a parameterised location on the screen, where ints, floats and dates are needed to be displayed. The following overloaded functions would be required:

  constream console;
  void display(int val, int x, int y)
  {
    console << setxy(x,y) << val;
  }
  void display(float val, int x, int y) {
    console << setxy(x,y) << val;
  }
  void display(date val, int x, int y) {
    console << setxy(x,y) << val;
  }

As you can see there is a fair amount of duplication in the coding of these functions. The following extract of code is a template of this function:

  constream console;
  template<class disptype>
  void display(disptype val, int x, int y)
  {
    console << setxy(x,y) << val;
  }

This strange function template obviously needs some elucidation, but I will postpone that until after the description of how this function template is used. The fol­lowing extract of code shows the usage for each of the three datatypes used in the first example.

  int p = 17;
  float f = 13.77;
  date today(12,12,92);
  display(p,2,2);
  display(f,2,4);
  display(today,2,6);

(In both the above examples I have assumed that date has a friend function to allow the correct printing of a date.)

The definition of the template function begins with 'template>class' followed by the internal name for the type of the argument and the closing angle brace. So far I have only declared that the following item (be it class or function) will be a template. The function is now declared in the normal manner, but using the in­ternal name for the type in the argument list.

An instance of the function will be created as the com­piler sees the first template function call for each da­tatype using the template function.

Template functions can be overloaded by convention­al functions if required. For example if the routine to display times needed to be displayed in braces. The following function will stop the compiler from generat­ing a template function for times, and use it instead.

  void display (time t, int x, int y)
  {
    console << setxy(x,y) << "[" << t << "]";
  }

Enough about template functions; they are very use­ful, but the real power of templates is in their ability to provide a further level of abstraction to classes and to make them non-datatype-dependent. In addition to the container class library, Borland provide a library of template based container classes. The coverage of these is beyond the scope of this article and will be covered in later articles.

In the next part of this article we will look at a template class. In order to demonstrate some of the power of the template class we will look at the problem encoun­tered with the calculation of standard deviations. It is a relatively simple task to create a class to handle the built-in datatypes using a normal class. But a single class to handle all datatypes, including user-created datatypes, is a little more difficult. However, with the use of templates, it is no more difficult than doing it for a simple built-in datatype.

The following template is a simplified version of the more complete and useful template found on the disk (see Working Classes for full details). In this version only the average is calculated; the disk version adds both sample standard deviation and population stand­ard deviation.

Note that this class uses a BIDS (Borland International Data Structures) template list for the storage of infor­mation and a list-iterator for moving through the list.

This example uses a time class to demonstrate that it is not only built-in datatypes that can have their aver­ages calculated, but any class that can be cast to long double and has a constructor that accepts a single long double parameter can have its standard devia­tion calculated.

The built-in datatype or class of a specific instance of the template is referred to as T. If an instance of int is used, 'T' is effectively replaced by 'int', likewise if an in­stance of the class 'time' is used, 'T' will be replaced by 'time'.

The template type is also passed on to the BIDS tem­plates to generate a list, of the type that replaces T.

  template<class T> class stat
  {
  protected:
    BI_ListImp<T> list;
  public:
    stat(void) {}
    void add(T&);
    T average(void);
  };

Note that the member function add adds a unit of type T to the list of type T. The member function average returns a unit of type T.

The definition of the class member functions starts with the template identifier, which precedes the return type.

  template void stat::add(T& x)
  {
    list.add(x);
  }
  template<class T> T stat<T>::average(void)
    // The List Iterator is used to 'walk' through
    // the list
    BI_ListIteratorImp<T> next(list);
    int i = 0;
    long double total = 0.0L;
    while (next)
    {
      total +=  (long double) next++;
    }
    total /= (long double)i;
    return T(total);
  }
  //
  // class of times
  //
  class tim
  {
  protected:
    int hr, mn, sc; public:
    tim(void) {hr = 0; mn = 0; sc = 0;}
    tim(int a, int b, int c) {hr = a; mn = b; sc = c;}
    tim(long double X)
    {
      long Y = (long)X;
      sc = Y%60L; Y/=60L; mn=Y%60L; hr=Y/60L;
    }
    operator long double ()
    {
      long double ti = 0.0L;
      ti = (long double)((hr*60+mn)*60+sc);
      return ti;
    }
    friend ostream& operator << (ostream&, tim&);
  };
  ostream& operator << (ostream& os, tim& t)
  {
    return os << t.hr << ":"
              << setw(2) << setfill('0') << t.mn
              << ":" << setw(2) << setfill('0')
              << t.sc;
  }

  // ----------------------------
  //   MAIN
  // ----------------------------

  void main(void) {
    clrscr();
    stat<float> x;
    float a=200.5, b=300.0, aa=220.4, bb=302.4;
    x.add(a); x.add(b); x.add(aa); x.add(bb);
    cout << "The items average "
         << x.average() << endl;
    stat<tim> y;
    tim c(2,3,4);
    tim d(4,5,6);
    y.add(c);
    y.add(d);
    cout << "The times average "
         << y.average() << endl;
  }

I hope that this article will persuade you to begin to use templates. They are not difficult to use, but they do re­quire a bit of careful planning. I know that once you start using them you will become hooked. They can be used to reduce the unnecessary inheritance when a base class contains the functionality, but the datatype dependent functions are identically programmed, with differing datatype in a set of derived classes.

As with all good things, you don't get something for nothing. There are drawbacks with functions. Often debuggers are unable to handle the parameterised types found in templates Another drawback is the re­quirement to include all the function source for the template class in the same compilation unit. This means that the template member functions are usual­ly copied in with the header file.






Your Privacy

By clicking "Accept Non-Essential Cookies" you agree ACCU can store non-essential cookies on your device and disclose information in accordance with our Privacy Policy and Cookie Policy.

Current Setting: Non-Essential Cookies REJECTED


By clicking "Include Third Party Content" you agree ACCU can forward your IP address to third-party sites (such as YouTube) to enhance the information presented on this site, and that third-party sites may store cookies on your device.

Current Setting: Third Party Content EXCLUDED



Settings can be changed at any time from the Cookie Policy page.