Tuesday, 10 March 2015

Templates in C++ with examples

Templates in Cpp with examples:

The main aim of templates are to allow the function or class to work in different data types without being rewritten for each one.

There are three types of templates available in C++ as shown below

Function templates
Class templates
variable templates

Function templates:

In C++, function templates are behave like functions that serve as a pattern for creating other similar functions. The basic idea behind function templates is to create a function without having to specify the exact type(s) of some or all of the variables. Instead, we define the function using placeholder types, called template type parameters.


The below is the simple syntax for the function template

template <class    identifier> function_declaration;

template <typename identifier> function_declaration;


Both expressions have exactly the same meaning and behave exactly the same way. The latter form was introduced to avoid confusion because a type parameter does not need to be a class, it may also be a basic type like int or double

Let’s take a look at the int version of max() again:
Creating the function templates by taking the Max function as an example:
Code without using templates
int max (int iX, int iY)
{
    return (iX > iY) ? iX : iY;
}

In the above example it verifies which value is maximum and returns that value, It allows only integer values, If we want to check for double or float value the above code snippet does not work.
To make the same function work with different datatypes we can go for function templates. The usage of a function template saves space in the source code file in addition to limiting changes to one function description and making the code easier to read.
 Type Max(Type TX, Type TY)

{
    return (TX > TY) ? TX : TY;
}

A template does not produce smaller object code, though, compared to writing separate functions for all the different data types used in a specific program. For example, if a program uses both an int and a double version of the max() function template shown above, the compiler will create an object code version of max() that operates on int arguments and another object code version that operates on double arguments. The compiler output will be identical to what would have been produced if the source code contained two separate non-templated versions of max(), one written to handle int and one written to handle double.

Using function templates:

Function template  usage is very straightforward,  we can use it just like any other function:
Let us see the simple function template
#include "stdafx.h"
#include <iostream>
using namespace std;
int main ()
{
    std::cout << max(13, 72) << std::endl;  // This will call max<int> by implicit argument deduction

    std::cout << max (13.0, 72.0) << std::endl;  // This will call max<double> by implicit argument deduction

    std::cout << max<double>(13, 72.0) << std::endl;// This call would be ambiguous, so in this case we explicitly instantiate max<double>

 getchar ();

 return 0;

} 

The output of the above program is
72

72

72

In the first two cases, the template argument Type is automatically deduced by the compiler to be int and double, respectively. In the third case automatic deduction of max(13, 72.0) would fail because the type of the parameters must in general match the template arguments exactly. Therefore we explicitly instantiate the double version with max<double>().
This function template can be instantiated with any copy-constructor type for which the expression y > x is valid. For user-defined types, this implies that the greater-than operator (>) must be overloaded in the type.

Drawbacks of Function template:

Template functions do have a few drawbacks, and we would be remiss not to mention them. First, older compilers generally do not have very good template support. However, modern compilers are much better at supporting and implementing template functionality properly. Second, template functions produce crazy-looking error messages that are much harder to decipher than those of regular functions.

But these drawbacks are minor compared to the power and flexibility of the templates.

Class templates

A class template definition looks like a regular class definition. The difference between the class template and the class is it is prefixed by the keyword template.

Look into the definition of the class template.
template <class T>
class TemplateStack
{
public:
 TemplateStack(int = 10) ;
 ~TemplateStack() { delete [] tpStkPointer ; }
 int PushVal(const T&);
 int PopVal(T&) ; 
 int isEmpty()const
    { return iTopVal == -1 ; }
 int isFull() const
    { return iTopVal == iCount - 1 ; }
private:
 int iCount ;
 int iTopVal ; 
 T* tpStkPointer ; 
} ;

T is a type parameter and it can be any type. For example, Stack<Token>, where Token is a user defined class. T does not have to be a class type as implied by the keyword class.

Class template member functions with implementation:

Implementing template member functions is somewhat different compared to the regular class member functions. The declarations and definitions of the class template member functions should all be in the same header file. The declarations and definitions need to be in the same header file. Consider the following.
#include "stdafx.h"

#include <iostream>
using namespace std;

template <class t>
class Base
{
public:
 Base () ;
 ~Base () ;
} ;
// Base.CPP

template <class t>
Base<t>::Base ()
{
}
template <class t>
Base<t>::~Base ()
{
}
//MAIN.CPP

int main ()
{
  Base<int> ociObj ;
  Base <float> ocfObj ;
  getchar ();
  return 0;
} 

When compiling Base.cpp, the compiler has both the declarations and the definitions available. At this point the compiler does not need to generate any definitions for template classes, since there are no instantiations. When the compiler compiles main.cpp, there are two instantiations: template class Base <int> and Base <float>. At this point the compiler has the declarations but no definitions!
While implementing class template member functions, the definitions are prefixed by the keyword template.
Now we can see the complete working example for StackTemplate with member functions:
 
#include "stdafx.h"
#include <iostream>
using namespace std;
template <class T>
class StackTemplate
{
public:
 StackTemplate (int = 10) ;
  ~StackTemplate ()
    { delete [] pchTPrt ; }
 int PushVal (const T&);
  int PopVal (T&) ; 
  int isEmpty ()const
    { return iTop == -1 ; }
 int isFull () const
    { return iTop == iCount - 1 ; }
private:
 int iCount ;
 int iTop ; 
 T* pchTPrt ; 
} ;
 template <class T>
StackTemplate <T>::StackTemplate (int s)
{
 iCount = s > 0 && s < 200 ? s : 20 ; 
 iTop = -1 ;
 pchTPrt = new T[iCount] ;
}
 /* This function is used to push the an element into the StackTemplate */

template <class T>
int StackTemplate <T>::PushVal(const T& iVal)
{
 if (!isFull ()) {
  pchTPrt[++iTop] = iVal ;
  return 1 ; 
 }
 return 0;
}
/* This function is used to PopVal an element from the StackTemplate */

template <class T>
int StackTemplate <T>::PopVal(T& popValue)
{
 if (!isEmpty())
 {
  popValue = pchTPrt[iTop--] ;
  popValue = pchTPrt[iTop--] ;
  return 1 ; 
 }
 return 0 ;
}

Verifying the above code by calling it from the main
int main ()
{
 typedef StackTemplate<float> foCStack;
 typedef StackTemplate<int> iOcStack;
 foCStack ocFStack (5) ;
 float fVal = 10.1 ;
 cout << "Pushing elements onto float stack vector" << endl;
  while (ocFStack.PushVal (fVal))
 {
  cout << fVal << ' ';
  fVal += 101.1;
 }
 cout << endl << "Stack is Full." << endl << endl << "Popping elements from float stack vecto" << endl;
  while (ocFStack.PopVal (fVal) == '\0')
  cout << fVal << ' ' ;
 cout << endl << "Stack is Empty" << endl;
 cout << endl ;
 iOcStack ocIStack;
 int iVal = 10.1;
 cout << "Pushing elements onto int stack" << endl;
  while (ocIStack.PushVal (iVal))
 {
  cout << iVal << ' ';
  iVal += 1;
 }
 cout << endl << "Stack is Full" << endl << endl << "Popping elements from int stack" << endl;
 while (ocIStack.PopVal (iVal) != '\0')
   cout << iVal << ' ';
 cout << endl << "Stack is Empty" << endl;
  getchar ();
  return 0;
}


The output generated by the above program is

Pushing elements onto float stack vector

10.1 111.2 212.3 313.4 414.5

Stack Full.

Popping elements from float stack vecto

Stack is Empty

Pushing elements onto int stack

10 11 12 13 14 15 16 17 18 19

Stack is Full

Popping elements from int stack

18 16 14 12 10

Stack is Empty



No comments: