Templates
 

Templates  is an important addition in C++ when compared with  C. Lot of readers have been sending queries about templates. I would try to explain a majority of them here in a question-answer form. The  questions have been suitably modified to address  a  general audience.

What are templates?

Templates  are  a mechanism for  generating  functions  and classes based on type parameters (templates are sometimes  called "parameterised  types").  By using templates, you  can  design  a single class that  operates  on  data  of  many types, instead of having  to create a separate class for each type.
For  example,  to create a type-safe function  that  returns  the minimum of two parameters without using templates, you would have to write a set of overloaded functions like this:

// min for ints
int min( int a, int b )
{
     return ( a < b ) ? a : b;
}

// min for longs
long min( long a, long b )
{
     return ( a < b ) ? a : b;
}

// min for chars
char min( char a, char b )
{
     return ( a < b ) ? a : b;
}
// etc...

By  using templates you can reduce this duplication to  a  single templated function as shown below:

template <class T> T min( T a,T b )
{
     return ( a < b ) ? a : b;
}

Templates can singnificantly reduce source code size and increase code flexibility without reducing type safety.

What are function templates?

With function templates, you can specify a set of  functions that  are based on the same code, but act on different  types  or classes.
For example:

template <class T> void MySwap( T& a, T& b)
{
     T c( a );
     a = b; b = c;
}

This  code  defines  a  family  of  functions  that  swap   their parameters.  From this template you can generate  functions  that will  swap  not only int and long types,  but  also user-defined types.  MySwap  will  even  swap  classes  if  the  class's  copy constructor and assignment operator are properly defined. In addition, the function template will prevent you from swapping objects of different types, since the compiler knows the types of the  a  and  b parameters at compile time.  Note  that all of the template  parameters  inside the angle brackets must be  used  as parameters for the templated function.

You  call  a  templated functions as you would  a  nontemplated function; no special syntax is needed. For example:

int i, j;
char k;
MySwap( i, j );      //OK
MySwap( i, k );      //Error, different types.

What do you mean by Function Template Instantiation?

When a templated function is first called for each type, the compiler creates an "instantiation", a specialized version of the templated function will be called every time the function is used
for the type. If you have several identical instantiations,  even in different modules, only one copy of the instantiation will end up in  the executable.

Standard type conversions are not applied to templated functions. Instead,  the  compiler first looks for an "exact match"  for  the parameters  supplied.  If this fails, it tries to  create  a  new instantiation  to create an "exact match". Finally, the  compiler attempts to apply overloading resolution to find a match for  the parameters. if this fails, the compiler generates an error.

What is a function template override?

With a templated function, you can define special  behaviour for  a  specific type by providing a non-templated  function  for that type. For example:

void MySwap( double a, double b );

This  declaration enables you to define a different function  for double  variables. Like other non-templated  functions,  Standard type  conversions(such as promoting a variable of type  float to double) are applied.

What are Class Templates?

You  can use class templates to create a family of  classes  that operate on a type. For example:

template <class T, int i> class ex
{
     public:
          ex ( void ) ;
          ~ex( void ) ;
          int setdata( T a, int b ) ;
     private:
          T Tarray[i] ;
          int arraysize ;
} ;

In  this example, the templated class uses two parameters, a type T  and an  int i. The T parameter can be passed any type, including  structures and classes. The i parameter has to be passed an integer constant. Since i  is a constant defined at compile time, you can define a member  array of size i using a standard automatic array declaration.

Unlike function templates, you do not use all template parameters in the definition of a templated class.

How do you define member functions of a template class?

Members  of template class are defined slightly differently  than those of nontemplated class. Continuing the preceding example:

template <class T, int i>
     int ex< T, i >::setdata( T a, int b )
     {
          if( ( b >= 0 ) && (b < i ) )
          {
               Tarray[b++] = a ;
               return sizeof( a ) ;
          }
          else
               return -1 ;
     }

Some more tips would be provided in the next episode.

Constructors and Destructors

Although  constructors  and  destructors  reference  the  name  of   the templated class twice, the template parameters should be referenced only once in the fully specified name.

template <class T, int i>
TempClass< T, i >::TempClass( void )
{
     TRACE( "TempClass created. \n" ) ;
}

template <class T, int i>
TempClass< T, i >::~TempClass( void )
{
     TRACE( "TempClass destroyed. \n" ) ;
}

Class Template Instantiation

Unlike function templates, when instantiating a class template, you must explicitly  instantiate  the  class by giving  the  parameters  for  the templated class.
To create an instance of TempClass:

TempClass< float, 6 > test1 ;        // OK
TempClass< char, items++ > test2 ;   // Error, second parameter
                                     // must be constant.

No  code is generated for a templated class ( or function ) until it  is instantiated.  Moreover, member functions are instantiated only if  they are  called. This can cause problems if you are building a library  with templates for other users.

Angle Bracket Placement

Bad  placement  of  angle brackets ( <> ) causes  many  template  syntax errors.  Make  sure  that  you use proper  spacing  and  parenthesis  to distinguish  angle  brackets  from operatores such as  >>  and  ->.
For example:

TempClass< float, a > b ? a : b > test1 ;

should be rewritten as

TempClass< float, ( a > b ? a : b ) > test1 ;

Similarly, pay extra attention when using macros that use angle brackets as template arguments.

When Should You Use Templates ?

Templates are often used to :

* Create a type-safe collection class ( for example, a stack ) that  can operate on data of any type.

*  Add extra type checking for functions that would otherwise take  void pointers.

*  Encapsulate  groups of operater overrides to modify type  behavior  ( such as smart pointers ) .

Most  of these uses can be implemented without templates  offer  several advantages:

* Templates are easier to write. You create only one generic version  of your class or function instead of manually creating specializations.

*  Templates  can  be easier to understand, since  they  can  provide  a straighforward way of abstracting type information.

*  Templates are type safe. Since the types that templates act upon  are known  at  compile-time, the compiler can perform type  checking  before errors occur.

Templates vs. Macros

In many ways, templates work like preprocessor macros, replacing the templated  variable  with  the  given  type.  However,  there  are  many differences between a macro like this:

# define min(i,j)  ((i) < (j)  ? (i) : (j))

and template:

template <class T>T min (T i, T j) { return (i<j) ? i:j)  }

Here are some problems with the macro :

*  There is no way for the compiler to verify that the  macro  parameter are fo compatible types.The macro is expanded without any specia;  type checking.

* The i and j parameter are evaluatedtwice . For example,if either parameter  has a postincremented variable, the incremented is  performed two times.

* Since macros are expanded by the preprocessor, compiler error messages will  refer  to  the expanded macro, rather than  the  macro  definition itself. Also the macro will show up in expanded form during debugging.

Templates vs. Void Pointers

Many functions that are now implemanted with void pointers can be implemented  with  templates.  Void pointers are  often  used  to  allow functions  to   operate  on data  of an unknown type.  When  using  void pointers,the  compiler  cannot distinguish types, so it  cannot  perform
type  checking  or type-specific behavior such  as  using  type-specific operator  overloading, or constructors and destructors .

With  templates,you  can create functions and classes  that  operate  on
typed  data.  The  type  looks abstracted  in  the  template  defintion.
Horwever, at compile-time the compiler creates a seperate version of the
function  for  each specified type. This enables the compiler  to  treat
templated  classes  and function as if they  acted  on  specific  types.
Templates can also imprve coding clarity, since you don't  need to create special cases for complex types such as structors.

Collection classes

templates are a good way of implementing  collection classes. Version 3.0 of the Microsoft Foundation Classes uses templates to implement six new collection   classes:   CArray,CMap,CTypedPtrArray,CTypedPtrList,   and CTypedPtrMap.  For information on using and   customisingthese  classes, see   the  "collections"articles  in  Programming  with  the   Microsoft Foundation Class Library.

The  Mystack  collection  is a simple implemention of  a  stack.The  two template parameter ,T and i, specify the type of elements in the stack  and  the   maximum number of that item in the stack. The  push  and  pop member  functions  add  and remove items in the stack,  with  the  stack growing from the bottoms of the stack.

templates <class T, int i> class Mystack
{
     T stackBuffer[i];
     int cItems;
public:
     void Mystack(void):cItems(i) {};
     void push(const T items );
     T pop (void);
};
template <class  T, int i> void Mystack< T,i>::push (const T items)
{
     if (cItems >0)
          stackbuffer[--cItems] = items;
     else
          throw " stack overflow error .";
     return;
}

template <class T,int i> T Mystack<T, i>::pop (void)
{
     if(cItems <i)
          return stackBuffer[cItems++]
     else
          throw "stack underflow error .";
}

Smart Pointers 

C++  allows  you  to create "smart  pointer"  classes  that  encapsulate pointer and override pointer oprators to add new funtionally to  pointer of almost any type. The following code outlines a simple refernces count garbage  collector. The templte class Ptr<T> implemented a garbage collecting pointer to any class derived from Refcount.
class Refcount {
     int crefs;
public:
     Refcount (void){crefs =0;}
     void  upcount(void){ ++crefs;}    
     void downcount(void){if(--crefs ==0)delete this ;}
};
class Sample : public Refcount {
public :
     void dosomething(void){ TRACE(did somethiing\n");}
};
template <class T> class Ptr {
     T* p;
public :
     Ptr(T* P_): p(p_){ p->upcount();}
     ~Ptr(void){ p->downcount();}
     operator T*(void){ return p;}
     T& oprator* (void){return *p}
     T* operator->(void){ return p;}
     Ptr& operator =(t*p_){
     p->upcount(); p=p_ ; p->downcount();return *this;
     }
};

int main() {
Ptr<Samples > p =new Samples; // samples #1
Ptr<Sample> p2 = new Samples;// samples #2
p=p2; // #1 has 0 refs, so it is destroyed ; #2 has two refs
p->doSomething();
return 0;
// As p2 and p go out of scope, their destructors call
// downcount. The cref varaible of #2 goes to  0,so #2 is
//destroyed


Classes  Refcount   and  Ptr<T>  together  provide  a  simple   garbage collection  solution for any class that can afford the int per  instance overhead to inherit from Refcount .Note that primaary benefit of using a parametric  class like Ptr <T> instead of a more generic class like  Ptr is the former is complete type-safe. The precending code ensures that a Ptr<T> can be used almost anywhere a T* is used; in contrast, a  generic Ptr would only provide implicit conversions to void*.

For  example,  if  this class is used  to  creat  and  manipulate garbage collected files, symbols, strings, and so forth. From the class template Ptr<T>, the compiler will create template classes Ptr<File>, Ptr<Symbol>, Ptr<String>, and so on, and their  member functions:   Ptr<File>::~Ptr(),  Ptr<File>::  operater   File*(),
Ptr<String>::~Ptr(), Ptr<String>::operator String*(), and so on.