Templates
=========
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;   
    x = y;
    y = temp;
}
generic version:
void swap( ..& x, ..& y)
{
    .. temp = x;   
    x = y;
    y = temp;
}
Templates are language-integrated version of macros:
template <typename T>
void swap( T& x, T& y)
{
    T temp = x;
    x = y;
    y = temp;
}
template <typename T>  
T max( T a, T b)
{
    if ( a > b )
        return a;
    else
        return b;
}
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; 
C<int, 25-15>   c3; 
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);     
    z = max( x, y);     
    k = max( i, ci);    
    z = max( i, x);     
Parameter deduction happens in compilation time.
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);     
    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);     
                        
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);    
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);             
    k = max<long, long, int>( i, x);    
    k = max<int, int, int>( i, j);      
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:
    
    
private:
    
    
};
Usage:
#include <matrix.h>
void f()
{
    Matrix<int>     mi(10,20);  
    Matrix<double>  md(20,40);  
    Matrix<bool>    mb(20,40);  
    
}
Examples:
#ifndef VECTOR_H
#define VECTOR_H
#include <stdexcept>
template <typename T>
class Vector
{
public:
  Vector();     
  Vector(const Vector& rhs);            
  Vector& operator=(const Vector& rhs); 
  ~Vector();    
  int     size() const;    
  T& at(int i);    
  T  at(int i) const; 
  T& operator[](int i);     
  T  operator[](int i) const;  
  void    push_back(T d);       
  void    pop_back();           
private:
  int     _size;        
  int     _capacity;    
  T*      _ptr;         
  void copy(const Vector& rhs);   
  void release();                 
  void grow();                    
};
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 )  
  {
    release();
    copy(rhs);
  }
  return *this;  
}
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 
#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>     
#include <stdexcept>
#include <iostream>
#include "vector.h"
template <typename T>   
class Stack;
template <typename T>                             
void print(std::ostream& os, const Stack<T>& ds); 
template <typename T>
class Stack
{
  friend void print<>(std::ostream&, const Stack&); 
public:
  
  
  int     size() const;    
  bool    empty() const;   
  T  top() const;     
  T& top();           
  void    push(T d);  
  void    pop();      
private:
  Vector<T>  _vec;    
};
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");  
  _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 
#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) 
  {
    std::cout << i << " ";
    for(int j = 0; j < 1000; ++j) 
    {
      Stack<double> z1, z2;
      z1.push(j); z2.push(j);
      z1 = z2;
    }
  }
  std::cout << "  ... ok" << std::endl;
  return 0;
}