Template (1) Molte volte l operato di una funzione o le proprietà di una classe non dipendono dal tipo dei dati coinvolti int sumvector(int *array, int n) { int sum(0); for (int i = 0; i < n; ++i) sum += array[i]; return (sum); double sumvector(double *array, int n) { double sum(0); for (int i = 0; i < n; ++i) sum += array[i]; return (sum); Il C++ permette, mediante l uso dei template, la definizione di funzioni e classi in modo parametrico rispetto ai tipi di dato coinvolti Laboratorio di Informatica Antonio Monteleone 131
Un template specifica una famiglia di Funzioni: funzioni template Classi: classi template Template (2) L uso dei template permette di scrivere il codice in modo del tutto generico, favorendone così il riutilizzo Una versione specifica di un template è detta istanza del template Il compilatore crea un opportuna istanza del template sostituendo ogni occorrenza di un parametro formale con il corrispondente parametro attuale Laboratorio di Informatica Antonio Monteleone 132
Funzioni Template (1) Una funzione template può essere parametrica rispetto al tipo del valore di ritorno, ai tipi dei suoi parametri, ai tipi delle sue variabili locali max.h template <class T> T max( T p1, T p2 ) { if ( p1 < p2 ) return p2; else return p1; parametri formali template <class T1, class T2, typename T3> return_type function( parameters ) { function_body Per il tipo T deve essere definito l operatore < #include max.h int main() { cout << max<int>(10,20) << endl; cout << max<float>(2.6,1.0) << endl; Laboratorio di Informatica Antonio Monteleone 133
Funzioni Template (2) E possibile effettuare l overload delle funzioni template template <class T> T max( T p1, T p2 ) {/*.. */ char *max(char *s1, char *s2) {/*.. */ template <class T> T max( T p1, T p2, T p3 ) {/*.. */ Nella definizione del template possono essere inclusi parametri numerici e parametri di default template <class T=int, int n=5> T fun (){ T temp[n]; int main() { //... int a = fun(); // parametri di default //... Laboratorio di Informatica Antonio Monteleone 134
int sumvector(int *array, int n) { int sum(0); for (int i = 0; i < n; ++i) sum += array[i]; return (sum); In modo del tutto generico: template <typename T> T sumvector(t *array, int n) { T sum; for (int i = 0; i < n; ++i) sum += array[i]; return (sum); Funzioni Template (3) double sumvector(double *array, int n) { double sum(0); for (int i = 0; i < n; ++i) sum += array[i]; return (sum); double darray[3] = {1.0, 2.2, 3.2; double dsum = sumvector(darray, 3); int iarray[5] = {1,5,6,2,8; int isum = sumvector(iarray,5); Nell uso: Laboratorio di Informatica Antonio Monteleone 135
Classi template (1) Una classe template permette di definire un nuovo tipo in modo parametrico sfruttando la genericità delle proprietà di tale tipo template <class T1, class T2, typename T3> class class_name { class_declaration ; template <class T> class Stack { int size, top; T *stackptr; public: Stack(int = 10); ~Stack(); bool push(const T &); bool pop(t &); ; parametri formali #include stack.h #include person.h int main() { Stack<int> intstack; intstack.push(8); Stack<Person> people(100); people.push(person( Rossi )); people.push(person( Bianchi )); Person p; people.pop(p); Laboratorio di Informatica Antonio Monteleone 136
Classi template (2) Nella definizione del template possono essere inclusi parametri numerici e parametri di default template <class T = int, int size = 10> class Stack { int size, top; T items[size]; public: Stack(); ~Stack(); bool push(const T &); bool pop(t &); ; #include stack.h #include person.h int main() { Stack<Person, 100> people; Stack<> intstack; // Laboratorio di Informatica Antonio Monteleone 137
Un esempio d uso di template: MyDbFieldT<T> (1) // in mydbfield.h // forward declaration class MyDbIntField; class MyDbDoubleField; class MyDbTextField; class MyDbDateField; class MyDbField { public: MyDbField(MyDbFieldType type) : m_type(type) { virtual ~MyDbField() { MyDbFieldType gettype() const { return m_type; virtual MyDbIntField *tointfield() {return 0; virtual MyDbDoubleField *todoublefield() {return 0; virtual MyDbTextField *totextfield() {return 0; virtual MyDbDateField *todatefield() {return 0; private: MyDbFieldType m_type; ; // tipi di campo ammissibili enum MyDbFieldType { MyDbFieldType_INT32 = 'I', MyDbFieldType_REAL = 'R', MyDbFieldType_TEXT = 'T', MyDbFieldType_DATE = 'D' ; Laboratorio di Informatica Antonio Monteleone 138
Un esempio d uso di template: MyDbFieldT<T> (2) // in mydbfield.h (continua) template <typename T> class MyDbFieldT : public MyDbField { public: MyDbFieldT(MyDbFieldType type, T value) : MyDbField(type), m_value(value) { void getvalue(t &value) { value = m_value; private: T m_value; ; //--- class MyDbIntField : public MyDbFieldT<int> { public: MyDbIntField(int val) : MyDbFieldT<int>(MyDbFieldType_INT32, val) { MyDbIntField *tointfield() { return this; ; Laboratorio di Informatica Antonio Monteleone 139
Un esempio d uso di template: MyDbFieldT<T> (3) // in mydbfield.h (continua) class MyDbDoubleField : public MyDbFieldT<double> { public: MyDbDoubleField(double val) : MyDbFieldT<double>(MyDbFieldType_REAL, val) { MyDbDoubleField *todoublefield() { return this; ; //---- class MyDbTextField : public MyDbFieldT<string> { public: MyDbTextField(string val) : MyDbFieldT<string>(MyDbFieldType_TEXT, val) { MyDbTextField *totextfield() { return this; ; Laboratorio di Informatica Antonio Monteleone 140
Un esempio d uso di template: MyDbFieldT<T> (4) // in mydbrecord.h #include <vector> #include mydbfield.h class MyDbRecord { public: MyDbRecord() { ~MyDbRecord() { Fields::iterator it = m_fields.begin(); for ( ; it!= m_fields.end(); ++it) delete *it; void add(mydbfield *field) { m_fields.push_back(field); int getfieldcount() const { return m_fields.size(); MyDbField *getfield(unsigned int index) const { assert(index>=0 && index<m_fields.size()); return m_fields[index]; private: typedef vector<mydbfield*> Fields; Fields m_fields; ; Laboratorio di Informatica Antonio Monteleone 141
Template di operatori relazionali (1) In generale è possibile implementare un qualunque operatore relazionale in termini di operator< (>,<=,>=) o operator== (!=) Poiché ciò è vero indipendentemente dal tipo specifico, l'implementazione di >, <=, >= e!= può essere effettuata in modo del tutto generico in termini di < e ==. // lab_relops.h namespace labinfo { template<typename T> bool operator!=(const T &a, const T &b) { return!(a == b); template<typename T> bool operator>(const T &a, const T &b) { return b < a; template<typename T> bool operator<=(const T &a, const T &b) { return!(b < a); template<typename T> bool operator>=(const T &a, const T &b) { return!(a < b); // namespace labinfo Laboratorio di Informatica Antonio Monteleone 142
#include lab_relops.h using namespace labinfo; class MyClass { int m_aa; public : MyClass(int aa) : m_aa(aa) { Template di operatori relazionali (2) friend bool operator<(const MyClass &a, const MyClass &b); ; bool operator<(const MyClass &a, const MyClass &b) { return a.m_aa < b.m_aa; int main() { MyClass a(12); MyClass b(20); assert(a < b && b > a); assert(b >= a && a <= b); Laboratorio di Informatica Antonio Monteleone 143
La classe MyClass definisce il solo operator<. Template di operatori relazionali (3) Sarà compito del compilatore istanziare gli operatori >, >= e <= a partire dalle corrispondenti funzioni template definite in lab_relops.h. operator!= non è istanziabile su MyClass poichémyclass non definisce operator== STL definisce proprio come funzioni template gli operatori relazionali!=, >, >= e <= all interno del namespace std::rel_ops. Per poterli utilizzare è necessario includere il file <utility> e riferirsi al namespace std::rel_ops mediante la direttiva using. #include <utility> using namespace std::rel_ops; // codice che utilizza gli operatori relazionali // Laboratorio di Informatica Antonio Monteleone 144
Specializzazione di template (1) Permette di scrivere una specifica implementazione per una particolare combinazione dei parametri del template La specializzazione esplicita di una funzione template viene fatta per motivi di efficienza per motivi di correttezza (la funzione template non fa la cosa giusta per una data combinazione dei parametri del template) Laboratorio di Informatica Antonio Monteleone 145
template<typename T> T *LinearSearch(T *array, int size, T const &v) { T *end = array+size; for ( ; array!= end; ++array) if (*array == v) return array; return 0; LinearSearch su char* non fa la cosa giusta Specializzazione di template (2) void foo() { int intarray[] = {1, 2, 3, 4; int *ii = LinearSearch(intArray, 4, 3); int pos = ii intarray; char sarray[]={"alfa", "beta", "gamma"; char *name = new char[100]; strcpy(name, "beta"); char *aa = LinearSearch(sArray, 3, name); Laboratorio di Informatica Antonio Monteleone 146
Specializzazione di template (3) E necessario specializzare la funzione LinearSearch<char *> perchè il comportamento della funzione template base non è quello atteso template<> char **LinearSearch<char*>(char **array, int size, char * const &v) { char **end = array+size; for ( ; array!= end; ++array) if (strcmp(*array, v)== 0) return array; return 0; Laboratorio di Informatica Antonio Monteleone 147
Template meta-programming I template permettono di precalcolare qualcosa a tempo di compilazione template<int N> struct Fact { enum { value = N*Fact<N-1>::value ; ; template<> struct Fact<1> { enum { value = 1 ; ; int main(int argc, char *argv[]) { int f = Fact<10>::value; //.. Laboratorio di Informatica Antonio Monteleone 148