Templates • Templates (plantillas) son patrones utilizados para generar código, que difiere de la forma normal de escribir código solamente en el uso simbólico del nombre de un tipo. • Por ejemplo, se podría requeir de una función llamada max que retorne el mayor de dos argumentos. – • Utilizando técnicas convencionales, se debería escribir una función para enteros, otra para dobles, y otra para cada tipo diferente para el que se quiera realizar dicha comparación. Los Templates permiten escribir una sola función y dejar que el compilador genere código específico para cada tipo para el que se requiera utilizar la función. template <class T> T max(const T &t1, const T &t2) { return ((t2 > t1) ? t2 : t1); } El compilador crea una instancia de la función para algún tipo específico al invocarla: int y = 1; int x = max(2,y); • POO Este proceso trabaja adecuadamente si los tipos utilizados tienen definidos las operaciones utilizadas de para la implementación de la función (en este caso la comparación de magnitud). 1 Template Functions Una función template es la declaración de una función, que utiliza un tipo no especificado T como un parámetro formal, valor de retorno, o variable local. template<class T> void Intercambiar(T &t1, T &t2) { T temp = t1; t1 = t2; t2 = temp; } Nota: Nada del procesamiento realizado en la función depende del tipo real representado por el parámetro genérico T. Esto es un requerimiento para que los templates tengan éxito. Otro requirimiento es que le tipo genérico, T, posea las operaciones utilziadas en la implementación de la función. En la función Intercambiar, se requiere que T tenga un operador de asignación operator=(...) y un constructor por copia, para crear un objeto temporal de tipo T; caso contrario versiones generadas por el compilador para construcción por copia y asignación deben ser válidos, lo que está garantizado solo para los tipos nativos del lenguaje. Si no se cumplen las condiciones mencionadas, la compilación de la función Intercambiar puede fallar, o, si el compilador puede generar los el construtor y el operador mencionados, la operación de la función puede ser incorrecta. POO 2 /////////////////////////////////////////////////////////// //// intercambiar.cpp – demuestra templates para // //// funciones // /////////////////////////////////////////////////////////// #include <iostream.h> template <class T> void Intercambiar(T &t1, T &t2) { T temp = t1; t1 = t2; t2 = temp; } void main() { int x = 1, y = 2; double u = 2.5, v = -3.3; cout << “Prueba de funcion basada en templates \n\n"; cout << " Prueba con enteros\n"; cout << " x = " << x << " and y = " << y << "\n"; cout << " Intercambiar(x,y)\n"; Intercambiar(x,y); cout << " x = " << x << " and y = " << y << "\n\n"; cout cout cout cout << << << << " " " " Prueba con doubles\n"; u = " << u << " and v = " << v << "\n"; Intercambiar(u,v)\n"; Intercambiar(u,v); u = " << u << " and v = " << v << "\n"; } POO 3 Prueba de funcion basada en templates Prueba con enteros x = 1 and y = 2 Intercambiar(x,y) x = 2 and y = 1 Prueba con doubles u = 2.5 and v = -3.3 Intercambiar(u,v) u = -3.3 and v = 2.5 POO 4 Clases Template Clases basadas en Templates son clases que dependen de un tipo NO específico para: – Un parámetro formal o valor de retorno para una función miembro. – Una variable local variable para una función miembro. – Un data miembro. Una clase basada en templates podría derivarse de una clase que dependa de algún tipo específico utilizando los siguientes criterios para re-escribir la clase: – Reemplazar class className { ... } con template<class T> class className { ... } – Para cada función miembro reemplace TipoDeRetorno className::memName { ... } con template<class T> TipoDeRetorno className<T>::memName { ... } en donde TipoDeRetorno es ya sea T o un tipo específico – Reemplazar toda instancia de la clase className obj; con className<type> obj; – Reemplazar todas las ocurrencias del tipo específico con el nombre del tipo genérico T A continiación se emplea una clase stack como un ejemplo para el uso de los criterios espresados anteriormente. El resultado es una clase genérica stack. POO 5 Template Class Declaration #ifndef TSTACK_H #define TSTACK_H // tstack.hpp - template stack class // modified from Effective C++, Scott //Meyers by Jim Fawcett #include <iostream> #include <cstdlib> template<class T> class stack { private: struct listNode { T data; listNode *next; // listNode constructor initializes node's data // and sets next ponter to point to next node listNode(const T& newdata, listNode *nextnode) : data(newdata), next(nextnode) { } }; listNode *top; public: stack(); // constructor ~stack(); // destructor void push(const T& object);// put elements on stack T pop(void); // remove elements }; POO 6 Template Member Functions in header file POO template<class T> stack<T>::stack() : top(0) { } template<class T> stack<T>::~stack(void) { while (top) { listNode *next_to_die = top; top = top->next; delete next_to_die; } } template<class T> void stack<T>::push(const T &object) { top = new listNode(object, top); if(!top) { std::cout << "\ndynamic memory exhausted\n"; exit(1); } } template<class T> T stack<T>::pop(void) { static T safe; if (!top) { std::cout << "\nattempt to pop empty stack<T>\n"; return safe; } listNode *save = top; top = top->next; int data = save->data; delete save; return data; } #endif 7 Template Instantiation // tstack.cpp - template stack class // modified from Effective C++, Scott Meyers using namespace std; #include "tstack.h" //----< test stub >-----------------------------------------// template<class T> void print_field(T t) { cout.width(5); cout << t; } void main() { cout << "\nTesting Generic stack<int> int_stack; int x=1, y=2, z=3; cout << "pushing " << x << int_stack.push(x); cout << "pushing " << y << int_stack.push(y); cout << "pushing " << z << int_stack.push(z); POO Stack Class\n\n"; " onto stack\n"; " onto stack\n"; " onto stack\n"; 8 Template Instantiation cout << "popping stack: "; print_field(int_stack.pop()); cout << "\npopping stack: "; print_field(int_stack.pop()); cout << "\npopping stack: "; print_field(int_stack.pop()); cout << "\npopping stack: "; int_stack.pop(); cout << endl; stack<double> dbl_stack; double a=1.5, b=2.5, c=3.5; cout << "pushing " << a << " onto stack\n"; dbl_stack.push(a); cout << "pushing " << b << " onto stack\n"; dbl_stack.push(b); cout << "pushing " << c << " onto POO stack\n"; dbl_stack.push(c); 9 Instantiation (continued) Testing Generic Stack Class pushing 1 onto stack pushing 2 onto stack pushing 3 onto stack popping stack: 3 popping stack: 2 popping stack: 1 popping stack: attempt to pop empty stack<T> pushing 1.5 onto stack pushing 2.5 onto stack pushing 3.5 onto stack popping stack: 3 popping stack: 2 popping stack: 1 popping stack: attempt to pop empty stack<T> POO 10