Templates
=========


// max function for integers, doubles, ...

int max( int a, int b)
{
    if ( a > b )
        return a;
    else
        return b;
}

double max( double a, double b)
{
    if ( a > b )
        return a;
    else
        return b;
}

 ... and so on ...


but what about the types we haven't written yet?
e.g. max( date, date)




- Preprocessor Macro



#define MAX(a,b)    a > b ? a : b

MAX( x, y)*2  -->   x > y ? x : y*2


#define MAX(a,b)    ((a) > (b) ? (a) : (b))

MAX( ++x, y) -->    ++x > y ? ++x : y



it does not always work, since macros are typeless:

void swap( int& x, int& y)
{
    int temp = x;   // type of temp is int
    x = y;
    y = temp;
}

generic version:

void swap( ..& x, ..& y)
{
    .. temp = x;   // type of temp is ??
    x = y;
    y = temp;
}



Templates are language-integrated version of macros:


// function template
template <typename T>
void swap( T& x, T& y)
{
    T temp = x;
    x = y;
    y = temp;
}


// another function template
template <typename T>  // same as: template <class T>
T max( T a, T b)
{
    if ( a > b )
        return a;
    else
        return b;
}


// a class template
template <typename T>
class Vector
{
public:
  ....
};



The template parameter can be a type, a const expression, or a pointer
to an external object or function.


Template is not a function, template is a pattern to generate a function
The concrete function is the "instance" created by automatic instantiation.


    double x, y = 5.1, z = 3.14;

    x = max( y, z);


The compiler "deduces" the parameter types, then creates a specific version.
The compiler "instantiates" the "specialization" with concrete types.




Templates must be placed into header files.
The compiler must read their source.




Type exquivalence:


Templates are the same type only if all type parameters are equivalent.

C<char,10>      c1;
C<int, 10>      c2; // different than c1
C<int, 25-15>   c3; // same as c2





Instantiation, parameter deduction:


    int     i = 3, j = 4, k;
    double  x = 3.14, y = 4.14, z;
    const int ci = 6;

    k = max( i, j);     // -> max(int, int)
    z = max( x, y);     // -> max(double, double)
    k = max( i, ci);    // -> max(int, int)

    z = max( i, x);     // -> error



Parameter deduction happens in compilation time.


// the two parameters are diffrent
template <class T, class S>
T max( T a, S b)
{
    if ( a > b )
        return a;
    else
        return b;
}


    int     i = 3;
    double  x = 3.14;

    z = max( i, x);     // ok, but...  not able to deduce parameters


    z == 3.0    // ??




template <class R, class T, class S>
R max( T a, S b)
{
    if ( a > b )
        return a;
    else
        return b;
}


    z = max( i, x);     // error, no parameter deduction on return value
                        // impossible to deduce R





// not nice, but works:
template <class R, class T, class S>
R max( T a, S b, R)
{
    if ( a > b )
        return a;
    else
        return b;
}


    z = max( i, x, 0.0);    // ok, but ugly and misleading




// Explicit specialization
template <class R, class T, class S>
R max( T a, S b)
{
    if ( a > b )
        return a;
    else
        return b;
}



    z = max<double>( i, x);             // ok, returns: 3.14
    k = max<long, long, int>( i, x);    // converts to long and int
    k = max<int, int, int>( i, j);      // ok, but unnecessary



Template overloading



template <class T>  T   max(T,T);
template <class R, class T, class S>    R   max(T,S);



User specializations


    char *s1 = "Hello";
    char *s2 = "world";

    cout << max( s1, s2);       // ??




template <>
template <> const char *max( const char *s1, const char *s2)
{
    return  strcmp( s1, s2) < 0;
}





Class templates
===============



template <typename T>
class Matrix
{
public:
    Matrix( int cols, int rows) : _t ( new T[cols * rows] ) {}
    Matrix( const Matrix& rhs);
    Matrix& operator=(const Matrix& rhs);
    ~Matrix();
    // ...
private:
    T* _t;
    // ..   
};


template <typename T>
Matrix<T>::Matrix( const Matrix& rhs) {  ... }
^          ^             ^
|          |             |
class   constr name   inside Matrix namespace this is Matrix<T>




Possible specializations:

template <>
class Matrix<bool>
{
public:
    // possibly different interface
    // ...
private:
    // possibly completely different implementations
    // ...  
};




Usage:

#include <matrix.h>


void f()
{
    Matrix<int>     mi(10,20);  // use generic version
    Matrix<double>  md(20,40);  // use generic version

    Matrix<bool>    mb(20,40);  // use bool specialization

    // ...
}




Examples:




#ifndef VECTOR_H
#define VECTOR_H

#include <stdexcept>


template <typename T>
class Vector
{
public:
  Vector();     // constructor

  Vector(const Vector& rhs);            // copy constructor
  Vector& operator=(const Vector& rhs); // assignment operator

  ~Vector();    // destructor

  int     size() const;    // actual size 

  T& at(int i);    // i-th element
  T  at(int i) const; // i-th element, const member

  T& operator[](int i);     // unchecked access
  T  operator[](int i) const;  // unchecked access, const member

  void    push_back(T d);       // append to end
  void    pop_back();           // remove from end;

private:
  int     _size;        // actual number of elements
  int     _capacity;    // buffer size
  T*      _ptr;         // pointer to buffer

  void copy(const Vector& rhs);   // private helper function
  void release();                 // private helper function
  void grow();                    // reallocate buffer
};

template <typename T>
Vector<T>::Vector()
{
  _capacity = 4;
  _size = 0;
  _ptr = new T[_capacity];
}

template <typename T>
Vector<T>::Vector(const Vector& rhs)
{
  copy(rhs);
}

template <typename T>
Vector<T>& Vector<T>::operator=(const Vector& rhs)
{
  if ( this != &rhs )  // avoid x = x
  {
    release();
    copy(rhs);
  }
  return *this;  // for x = y = z
}

template <typename T>
Vector<T>::~Vector()
{
  release();
}

template <typename T>
void Vector<T>::copy(const Vector& rhs)
{
  _capacity = rhs._capacity;
  _size = rhs._size;
  _ptr = new T[_capacity];

  for (int i = 0; i < _size; ++i)
    _ptr[i] = rhs._ptr[i];
}

template <typename T>
void Vector<T>::release()
{
  delete [] _ptr;
}

template <typename T>
void Vector<T>::grow()
{
  T *_oldptr = _ptr;
  _capacity = 2 * _capacity;
  _ptr = new T[_capacity];

  for ( int i = 0; i < _size; ++i)
    _ptr[i] = _oldptr[i];

  delete [] _oldptr;
}

template <typename T>
int Vector<T>::size() const
{
  return _size;
}

template <typename T>
T& Vector<T>::at(int i)
{
  if ( i >= _size )
    throw std::out_of_range("bad index");

  return _ptr[i];
}

template <typename T>
T Vector<T>::at(int i) const
{
  if ( i >= _size )
    throw std::out_of_range("bad index");

  return _ptr[i];
}

template <typename T>
T& Vector<T>::operator[](int i)
{
  return _ptr[i];
}

template <typename T>
T Vector<T>::operator[](int i) const
{
  return _ptr[i];
}

template <typename T>
void Vector<T>::push_back(T d)
{
  if ( _size == _capacity )
    grow();

  _ptr[_size] = d;
  ++_size;
}

template <typename T>
void Vector<T>::pop_back()
{
  if ( 0 == _size )
    throw std::out_of_range("vector empty");

  --_size;
}


#endif /* VECTOR_H */






#include <iostream>
#include <string>
#include "vector.h"


int main()
{
  Vector<double> x;

  for (int i = 0; i < 10; ++i )
    x.push_back(i);

  Vector<double> y;

  for (int i = 0; i < 15; ++i )
    y.push_back(i+20);

  std::cout << "x = [ ";
  for (int i = 0; i < x.size(); ++i )
    std::cout << x.at(i) << " ";
  std::cout << "]" << std::endl;

  std::cout << "y = [ ";
  for (int i = 0; i < y.size(); ++i )
    std::cout << y.at(i) << " ";
  std::cout << "]" << std::endl;

  x = y;
  std::cout << "x = y" << std::endl;

  std::cout << "x = [ ";
  for (int i = 0; i < x.size(); ++i )
    std::cout << x[i] << " ";
  std::cout << "]" << std::endl;

  std::cout << "y = [ ";
  for (int i = 0; i < y.size(); ++i )
    std::cout << y[i] << " ";
  std::cout << "]" << std::endl;

  x[0] = 999;
  std::cout << "x[0] = 999" << std::endl;

  std::cout << "x = [ ";
  for (int i = 0; i < x.size(); ++i )
    std::cout << x[i] << " ";
  std::cout << "]" << std::endl;

  std::cout << "y = [ ";
  for (int i = 0; i < y.size(); ++i )
    std::cout << y[i] << " ";
  std::cout << "]" << std::endl;

  std::cout << "Vector<std::string>" << std::endl;
  Vector<std::string> vs;
  for (int i = 0; i < 10; ++i )
  {
    std::string s = "str";
    s += char(i+65);
    vs.push_back(s);
  }
  std::cout << "vs = [ ";
  for (int i = 0; i < vs.size(); ++i )
    std::cout << vs[i] << " ";
  std::cout << "]" << std::endl;

  return 0;
}






#ifndef STACK_H
#define STACK_H

#include <iosfwd>     // only declarations of <iostream>
#include <stdexcept>
#include <iostream>
#include "vector.h"

template <typename T>   // forward declaration for Stack<T>
class Stack;

template <typename T>                             // forward declaration
void print(std::ostream& os, const Stack<T>& ds); // of the print function

template <typename T>
class Stack
{
  friend void print<>(std::ostream&, const Stack&); // give access rights 

public:
  // no need for special member functions, 
  // Vector works as it is

  int     size() const;    // actual size 
  bool    empty() const;   // size() == 0

  T  top() const;     // read top element
  T& top();           // access top element

  void    push(T d);  // append to end
  void    pop();      // remove from end;

private:
  Vector<T>  _vec;    // reuse Vector
};


template <typename T>
int Stack<T>::size() const
{
  return _vec.size();
}

template <typename T>
bool Stack<T>::empty() const
{
  return 0 == _vec.size();
}

template <typename T>
void Stack<T>::push(T d)
{
  _vec.push_back(d);
}

template <typename T>
void Stack<T>::pop()
{
  if ( 0 == _vec.size() )
    throw std::out_of_range("stack empty");  // instead of "vector empty"

  _vec.pop_back();
}

template <typename T>
T Stack<T>::top() const
{
  if ( 0 == _vec.size() )
    throw std::out_of_range("stack empty");

  return _vec[_vec.size()-1];
}

template <typename T>
T& Stack<T>::top()
{
  if ( 0 == _vec.size() )
    throw std::out_of_range("stack empty");

  return _vec[_vec.size()-1];
}

template <typename T>
void print(std::ostream& os, const Stack<T>& ds)
{
  os << "[ ";
  for (int i = 0; i < ds._vec.size(); ++i)
    os << ds._vec[i] << " ";
  os << "]" << std::endl;
}

#endif /* STACK_H */





#include <iostream>
#include "stack.h"


int main()
{
  Stack<double> x;

  for (int i = 0; i < 10; ++i )
    x.push(i);

  Stack<double> y;

  for (int i = 0; i < 15; ++i )
    y.push(i+20);

  print( std::cout, x);
  print( std::cout, y);

  x = y;
  std::cout << "x = y" << std::endl;

  print( std::cout, x);
  print( std::cout, y);

  x.top() = 999;
  std::cout << "x.top() = 999" << std::endl;

  print( std::cout, x);
  print( std::cout, y);

  std::cout << "stress test for memory leaks..." << std::endl;
  for(int i = 0; i < 1000; ++i) // set to 100000 for real test
  {
    std::cout << i << " ";
    for(int j = 0; j < 1000; ++j) // set to 100000 for real test
    {
      Stack<double> z1, z2;
      z1.push(j); z2.push(j);
      z1 = z2;
    }
  }
  std::cout << "  ... ok" << std::endl;

  return 0;
}