Un template (sablon) este o unealta de programare de uz general, care permite reutilizarea codului sursa! Prin sabloane este implementat conceptul de "tip parametrizat".

Cuvantul cheie template instruieste compilatorul ca definitia de clasa care urmeaza va manipula unul sau mai multe tipuri neprecizate; la momentul generarii codului clasei, aceste tipuri vor trebui precizate:


template <class T> class array{
	enum {size=100};
	T A[size];
	public:
	T& operator[] (int index){
		return A[index];
	};
};
int main(){
	array <int> ia;
	array <float> fa;
	for(int i=0; i<10; i++){
		ia[i]=i*i;
		fa[i]=float(i)*1.4;
	}
}
Daca definitia unei functii membru este data in afara clasei, este necesara prezenta declaratiei de template; de exemplu:

template<class T> T& array<T>::operator[] (int index){
	return A[index];
};
Compilatorul nu aloca memorie la intalnirea unei definitii de template; el asteapta specializarea acesteia (adica furnizarea de argumente pentru tipurile neprecizate). Din acest motiv, un fisier header poate contine atat declaratia, cat si definitia unui template, fara a aparea problema definitiilor multiple (prin includeri repetate ale header-ului in fisiere sursa *.cpp).

Spre deosebire de functiile obisnuite, definitia unei functii sablon trebuie sa urmeze declaratiei! Parametrii sunt implicit dedusi din tipul argumentelor.


template <class T> T max(T t1, T t2){
	return (t1 > t2) ? t1 : t2;
};
Este posibila supraincarcarea functiilor sablon si combinarea lor cu functii obisnuite, care au acelasi nume. (intr-un astfel de caz, compilatorul nu specializeaza functia sablon, ci apeleaza varianta uzuala!)

Argumentele unui sablon pot fi si tipuri predefinite; astfel, se pot utiliza valori implicite! La compilare, valorile argumentelor trebuie sa fie constante sau expresii constante de tip intreg; valoarea nu este pastrata intern in clasa generata, dar poate fi utilizata ca si cum ar fi o data membru.


template <class T, int size=100> class array{
	T A[size];
	public:
	T& operator[] (int index) {/**/};
	int lenght() const {
		return size; //!
	}
}
array <char, 5> ca; //OK
const int cn = 5;
array <float, cn> fa; //OK
array <unsignde char, sizeof(float)> uca; //OK
int num = 10;
array <bool, num> ba; //not OK
Se pot accepta ca argumente si pointeri la date membru, adrese de obiecte sau adrese de functii, cu conditia ca acestea sa aiba linkage extern.

template <class T, const char*> class A {/**/};
const char* p = "illegal";
A <int, "invalid"> a; //eroare, linkage intern
A <int, p> b; //eroare, linkage intern
Un sablon poate lua ca argument un alt sablon (sintactic, un spatiu intre cele doua paranteze unghiulare este obligatoriu!):

Vector <Vector<char*> > msg_que(10);
Se recomanda utilizarea lui typedef:

typedef Vector<char*> msg;
Vector<msg> msg_que(10);
Un template poate avea date membru statice:

template <class T> class C{
	public:
	static T stat;
};
template <class T> T C<T>::stat = 5; //definitie
int n = C<int>::stat; //acces
Printr-un prieten al unui sablon se poate intelege:

template <class T> class C{/**/};
template <class T> class Vector1{
	...
	friend class C<void*>; //alte specializari ale lui C nu sunt prietene!
	...
};
template <class T> class Vector2{
	...
	template <class T> friend class C; //orice specializare a lui C este prietena
	//fiecarei specializari a lui Vector2!
	...
};
Exemplu: clasa care implementeaza o stiva.

template <class T> class stack{
	struct link{
		T* data;
		link* next;
		link(T* d, link* nxt): data(d), next(nxt) {};
	} *head; //varful stivei
	public:
	stack(): head(0){}; //stiva vida
	
	~stack(){
		while(head) delete pop();
	};
	
	void push(T* dat){
		head = new link(dat, head);
	};

	T* peek() const {
		return head? head->data : 0;
	};

	T* pop(){
		if (head==0) return 0;
		T* result = head->data;
		link* oldhead = head;
		head = head->next;
		delete oldhead;
		return result;
	};
};

int main(){
	ifstream in("exemplu.txt");
	stack<string> textlines;
	string line;
	while (getline(in, line))
		textlines.push(new string(line)); //copie in stiva
	string *s;
	for(int i=0; i<10; i++){
		if ((s = (string*) textlines.pop()) == 0) break; //stiva vida?
		cout << *s << endl;
		delete s;
	}
}
Exemplu: clasa care implementeaza un vector.

template <class T> class Vector{
	size_t sz;
	T* buff;
	public:
	explicit Vector<T>(size_t s=100);
	Vector<T>(const Vector<T>&);
	Vector<T>& operator= (const Vector<T>&);
	~Vector<T>();
	T& operator[](unsigned int index);
	const T& operator[] (unsigned int index) const;
	size_t size() const;
};

template <class T> Vector<T>::Vector<T>(size_t s):
	sz(s), buff(new T[s]){};

template <class T> Vector<T>::Vector<T>(const Vector<T>& v){
	sz = 0;
	buff = 0;
	*this = v; //operatorul "=" supraincarcat
};

template <class T> Vector<T>& Vector<T>::operator= (const Vector <T>& v){
	if (this == &v) return *this;
	this->Vector<T>::~Vector<T>(); //apel destructor
	buff = new T[v.size()];
	for(size_t i=0; i< v.size(); i++) buff[i] = v[i];
	sz = v.size();
	return *this;
};

template <class T> Vector<T>::~Vector<T>{
	delete [] buff;
};

template <class T> inline T& Vector<T>::operator[](unsigned int i){
	return buff[i];
};

template <class T> inline const T& Vector<T>::operator[](unsigned int i) const{
	return buff[i];
};

template <class T> inline size_t Vector<T>::size() const {
	return sz;
};
idee.jpg - 2332 Bytes Compilatorul implementeaza pentru o clasa sablon doar functiile membru necesare pentru o specializare data! Astfel:

int main(){
	Vector<int> vi(5); //cod constructor
	for (int i = 0; i < 5; i++){
		v[i] = i; //cod operator []
		cout << v[i] << end;
	};
	return 0; //cod destructor
}
Codul pentru functia membru size() nu este generat de catre compilator deoarece in programul anterior acesta functie membru nu este utilizata. Acesta politica, cunoscuta sub numele de generate on demand, face parte din standard si asigura eficienta si flexibilitatea. (De exemplu, o clasa sablon container afiseaza tipul obiectului utilizand versiunea supraincarcata a operatorului << pentru obiectul respectiv. Un astfel de container poate pastra si obiecte pentru care nu exista o versiune supraincarcata a lui <<, daca programul nu invoca <<)

O clasa template poate avea specializari partiale ca alternativa la specializarea primara. De exemplu, o clasa Vector poate trata altfel elementele de tip pointer (metode implementate utilizand operatorul ->).

O specializare partiala este indicata de o lista de parametri care urmeaza numelui clasei; de exemplu:


template <class T, class U, int i> class A{}; //specializarea primara
template <classT, int i> class A<T, T*, i>{};//specializare partiala
template <class T> class A<int, T*, 8>{}; //alta specializare partiala
Specializarile partiale trebuie sa apara imediat dupa declaratia specializarii primare a unei clase template. O specializare partiala este utilizata in locul celei primare daca argumentele se potrivesc.

O specializare explicita este un alt mod de a da o alternativa la o specializare primara. Este utilizata atunci cand argumentele unei specializari particulare se potrivesc cu cele date in specializarea explicita. Se utilizeaza prefixul template<>.


template <> class Vector<bool> {/**/};
Versiuni specializate ale functiilor dintr-o clasa sablon pot fi alese de catre compilator. Declaratia versiunii specializate este doar recomandabil a fi inclusa in declaratia clasei; definitia functiei specializate trebuie sa apara dupa definitia functiei generice!

template <class T> class vector;
template <class T> bool operator == (const vector<T>&, const vector<T>&){/**/};
template <class T> class vector{
	//...
	public:
	friend bool operator ==<T>(const vector<T>&, const vector<T>&);
	friend bool operator == (const vector<const char*>&, const vector<const char*>&);
};
template<> bool operator == (const vector<const char*>& v1, const vector<const char*>& v2){
	if (v1.size() != v2.size()) return false;
	for(size_t i = 0; i < v1.size(); i++)
		if ((strcmp(v1[i], v2[i]) != 0) return false;
	return true;
};