The template in C++ language is just a variant of macro.
//arraytp.h -- Array Template
#ifndef ARRAYTP_H_
#define ARRAYTP_H_
#include <iostream>
using namespace std;
#include <cstdlib>
template <class T, int n>
class ArrayTP
{
private:
T ar[n];
public:
ArrayTP() {};
explicit ArrayTP(const T & v);
virtual T & operator[](int i);
virtual const T & operator[](int i) const;
};
template <class T, int n>
ArrayTP<T,n>::ArrayTP(const T & v)
{
for (int i = 0; i < n; i++)
ar[i] = v;
}
template <class T, int n>
T & ArrayTP<T,n>::operator[](int i)
{
if (i < 0 || i >= n)
{
cerr << "Error in array limits: " << i
<< " is out of range/n";
exit(1);
}
return ar[i];
}
template <class T, int n>
const T & ArrayTP<T,n>::operator[](int i) const
{
if (i < 0 || i >= n)
{
cerr << "Error in array limits: " << i
<< " is out of range/n";
exit(1);
}
return ar[i];
}
#endif
The parameter "n" has some restrictions. It can be an integral type, an enumeration type, a reference,
or a pointer. Thus, double m is ruled out, but double &rm and double *pm are allowed. Also, the template
code can't alter the value of the argument or take its address. Thus, in the ArrayTP template, expressions
such as n++ or &n would not be allowed. Also, when you instantiate a template, the value used for the expression
argument should be a constant expression.
Template Specializations
Implicit Instantiations
ArrayTb<int, 100> stuff; // implicit instantiation
The compiler doesn't generate an implicit instantiation of the class until it needs an object:
ArrayTb<double, 30> * pt; // a pointer, no object needed yet
pt = new ArrayTb<double, 30>; // now an object is needed
Explicit Instantiations
The compiler generates an explicit instantiation of a class declaration when you declare a class using the
keyword template and indicating the desired type or types. The declaration should be in the same namespace as
the template definition. For example, the declaration
template class ArrayTb<String, 100>; // generate ArrayTB<String, 100> class
In this case the compiler generates the class definition, including method definitions, even though no object
of the class has yet been created or mentioned. Just as with the implicit instantiation, the general template is
used as a guide to generate the specialization.
Explicit Specializations
A specialized class template definition has the following form:
template <> class Classname<specialized-type-name> { ... };
template <> class SortedArray<char *>
{
...// details omitted
};
SortedArray<int> scores; // use general definition
SortedArray<char *> dates; // use specialized definition
Partial Specializations
// general template
template <class T1, class T2> class Pair {...};
// specialization with T2 set to int
template <class T1> class Pair<T1, int> {...};
// specialization with T1 and T2 set to int
template <> class Pair<int, int> {...};
Pair<double, double> p1; // use general Pair template
Pair<double, int> p2; // use Pair<T1, int> partial specialization
Pair<int, int> p3; // use Pair<int, int> explicit specialization
// general template
template <class T1, class T2, class T3> class Trio{...};
// specialization with T3 set to T2
template <class T1, class T2> class Trio<T1, T2, T2> {...};
// specialization with T3 and T2 set to T1*
template <class T1> class Trio<T1, T1*, T1*> {...};
Trio<int, short, char *> t1; // use general template
Trio<int, short> t2; // use Trio<T1, T2, T2>
Trio<char, char *, char *> t3; use Trio<T1, T1*, T1*>
Member Templates
// tempmemb.cpp -- template members
#include <iostream>
using namespace std;
template <typename T>
class beta
{
private:
template <typename V> // nested template class member
class hold
{
private:
V val;
public:
hold(V v = 0) : val(v) {}
void show() const { cout << val << endl; }
V Value() const { return val; }
};
hold<T> q; // template object
hold<int> n; // template object
public:
beta( T t, int i) : q(t), n(i) {}
template<typename U> // template method
U blab(U u, T t) { return (n.Value() + q.Value()) * u / t; }
void Show() const {q.show(); n.show();}
};
int main()
{
beta<double> guy(3.5, 3);
guy.Show();
cout << guy.blab(10, 2.3) << endl;
cout << "Done/n";
return 0;
}
Or another way define a memeber template.
template <typename T>
class beta
{
private:
template <typename V> // declaration
class hold;
hold<T> q;
hold<int> n;
public:
beta( T t, int i) : q(t), n(i) {}
template<typename U> // declaration
U blab(U u, T t);
void Show() const {q.show(); n.show();}
};
// member definition
template <typename T>
template<typename V>
class beta<T>::hold
{
private:
V val;
public:
hold(V v = 0) : val(v) {}
void show() const { cout << val << endl; }
V Value() const { return val; }
};
// member definition
template <typename T>
template <typename U>
U beta<T>::blab(U u, T t)
{
return (n.Value() + q.Value()) * u / t;
}
The definitions have to identify T, V, and U as template parameters. Because the templates are nested, you have
to use the
template <typename T>
template <typename V>
syntax instead of the
template<typename T, typename V>
syntax.
Templates As Parameters
// tempparm.cpp -- template template parameters
#include <iostream>
using namespace std;
#include "stacktp.h"
template <template <typename T> class Thing>
class Crab
{
private:
Thing<int> s1;
Thing<double> s2;
public:
Crab() {};
// assumes the thing class has push() and pop() members
bool push(int a, double x) { return s1.push(a) && s2.push(x); }
bool pop(int & a, double & x){ return s1.pop(a) && s2.pop(x); }
};
int main()
{
Crab<Stack> nebula;
// Stack must match template <typename T> class thing
int ni;
double nb;
while (cin>> ni >> nb && ni > 0 && nb > 0)
{
if (!nebula.push(ni, nb))
break;
}
while (nebula.pop(ni, nb))
cout << ni << ", " << nb << endl;
cout << "Done./n";
return 0;
}
The Thing<int> is instantiated as Stack<int> and Thing<double> is instantiated as Stack<double>. In short,
the template parameter Thing is replaced by whatever template type is used as a template argument in declaring
a Crab object.
//arraytp.h -- Array Template
#ifndef ARRAYTP_H_
#define ARRAYTP_H_
#include <iostream>
using namespace std;
#include <cstdlib>
template <class T, int n>
class ArrayTP
{
private:
T ar[n];
public:
ArrayTP() {};
explicit ArrayTP(const T & v);
virtual T & operator[](int i);
virtual const T & operator[](int i) const;
};
template <class T, int n>
ArrayTP<T,n>::ArrayTP(const T & v)
{
for (int i = 0; i < n; i++)
ar[i] = v;
}
template <class T, int n>
T & ArrayTP<T,n>::operator[](int i)
{
if (i < 0 || i >= n)
{
cerr << "Error in array limits: " << i
<< " is out of range/n";
exit(1);
}
return ar[i];
}
template <class T, int n>
const T & ArrayTP<T,n>::operator[](int i) const
{
if (i < 0 || i >= n)
{
cerr << "Error in array limits: " << i
<< " is out of range/n";
exit(1);
}
return ar[i];
}
#endif
The parameter "n" has some restrictions. It can be an integral type, an enumeration type, a reference,
or a pointer. Thus, double m is ruled out, but double &rm and double *pm are allowed. Also, the template
code can't alter the value of the argument or take its address. Thus, in the ArrayTP template, expressions
such as n++ or &n would not be allowed. Also, when you instantiate a template, the value used for the expression
argument should be a constant expression.
Template Specializations
Implicit Instantiations
ArrayTb<int, 100> stuff; // implicit instantiation
The compiler doesn't generate an implicit instantiation of the class until it needs an object:
ArrayTb<double, 30> * pt; // a pointer, no object needed yet
pt = new ArrayTb<double, 30>; // now an object is needed
Explicit Instantiations
The compiler generates an explicit instantiation of a class declaration when you declare a class using the
keyword template and indicating the desired type or types. The declaration should be in the same namespace as
the template definition. For example, the declaration
template class ArrayTb<String, 100>; // generate ArrayTB<String, 100> class
In this case the compiler generates the class definition, including method definitions, even though no object
of the class has yet been created or mentioned. Just as with the implicit instantiation, the general template is
used as a guide to generate the specialization.
Explicit Specializations
A specialized class template definition has the following form:
template <> class Classname<specialized-type-name> { ... };
template <> class SortedArray<char *>
{
...// details omitted
};
SortedArray<int> scores; // use general definition
SortedArray<char *> dates; // use specialized definition
Partial Specializations
// general template
template <class T1, class T2> class Pair {...};
// specialization with T2 set to int
template <class T1> class Pair<T1, int> {...};
// specialization with T1 and T2 set to int
template <> class Pair<int, int> {...};
Pair<double, double> p1; // use general Pair template
Pair<double, int> p2; // use Pair<T1, int> partial specialization
Pair<int, int> p3; // use Pair<int, int> explicit specialization
// general template
template <class T1, class T2, class T3> class Trio{...};
// specialization with T3 set to T2
template <class T1, class T2> class Trio<T1, T2, T2> {...};
// specialization with T3 and T2 set to T1*
template <class T1> class Trio<T1, T1*, T1*> {...};
Trio<int, short, char *> t1; // use general template
Trio<int, short> t2; // use Trio<T1, T2, T2>
Trio<char, char *, char *> t3; use Trio<T1, T1*, T1*>
Member Templates
// tempmemb.cpp -- template members
#include <iostream>
using namespace std;
template <typename T>
class beta
{
private:
template <typename V> // nested template class member
class hold
{
private:
V val;
public:
hold(V v = 0) : val(v) {}
void show() const { cout << val << endl; }
V Value() const { return val; }
};
hold<T> q; // template object
hold<int> n; // template object
public:
beta( T t, int i) : q(t), n(i) {}
template<typename U> // template method
U blab(U u, T t) { return (n.Value() + q.Value()) * u / t; }
void Show() const {q.show(); n.show();}
};
int main()
{
beta<double> guy(3.5, 3);
guy.Show();
cout << guy.blab(10, 2.3) << endl;
cout << "Done/n";
return 0;
}
Or another way define a memeber template.
template <typename T>
class beta
{
private:
template <typename V> // declaration
class hold;
hold<T> q;
hold<int> n;
public:
beta( T t, int i) : q(t), n(i) {}
template<typename U> // declaration
U blab(U u, T t);
void Show() const {q.show(); n.show();}
};
// member definition
template <typename T>
template<typename V>
class beta<T>::hold
{
private:
V val;
public:
hold(V v = 0) : val(v) {}
void show() const { cout << val << endl; }
V Value() const { return val; }
};
// member definition
template <typename T>
template <typename U>
U beta<T>::blab(U u, T t)
{
return (n.Value() + q.Value()) * u / t;
}
The definitions have to identify T, V, and U as template parameters. Because the templates are nested, you have
to use the
template <typename T>
template <typename V>
syntax instead of the
template<typename T, typename V>
syntax.
Templates As Parameters
// tempparm.cpp -- template template parameters
#include <iostream>
using namespace std;
#include "stacktp.h"
template <template <typename T> class Thing>
class Crab
{
private:
Thing<int> s1;
Thing<double> s2;
public:
Crab() {};
// assumes the thing class has push() and pop() members
bool push(int a, double x) { return s1.push(a) && s2.push(x); }
bool pop(int & a, double & x){ return s1.pop(a) && s2.pop(x); }
};
int main()
{
Crab<Stack> nebula;
// Stack must match template <typename T> class thing
int ni;
double nb;
while (cin>> ni >> nb && ni > 0 && nb > 0)
{
if (!nebula.push(ni, nb))
break;
}
while (nebula.pop(ni, nb))
cout << ni << ", " << nb << endl;
cout << "Done./n";
return 0;
}
The Thing<int> is instantiated as Stack<int> and Thing<double> is instantiated as Stack<double>. In short,
the template parameter Thing is replaced by whatever template type is used as a template argument in declaring
a Crab object.