Bienvenidos sean a este post, hoy veremos el tercer tipo de parametros para templates.
En los dos posts anteriores vimos dos tipos de parametros para nuestros templates. Este nuevo tiene la capacidad de usar otros templates como tipos, tal como su nombre lo indica, permitiendo otras conductas. Veamos como son sus sintaxis:
template < lista parametros > class id;
template < lista parametros > class id = valor_predet;
template < lista parametros > class... id;
Entre los signos mayores y menores pasaremos todos los parametros que usaremos, en esto es igual a todo lo visto anteriormente pero entre ellos podemos pasar el id de otros template definidos anteriormente. Luego llevara la palabra class y el nombre identificador, aunque este al igual que vimos en el post anterior, es opcional y podemos no informarlo. Pero esta forma de trabajar esta pensada para archivos de encabezados donde se encuentren prototipos de las clases. Tambien podemos asignarle un valor predeterminado, esta puede ser un tipo o una clase definida por el usuario. Y por ultimo, tambien podemos usar a template variado para poder manejar mas datos. Analicemos el primer ejemplo para enttender el concepto:
template <typename T>
class Objeto {};
template<class K, class V, template <typename> class C = Objeto>
class Map
{
C<K> clave;
C<V> valor;
};
Este es un ejemplo simple donde primero definiremos una clase con su template, simple y generica sin mas que eso, y luego definiremos otro template. Donde primero pasaremos dos parametros normales y el tercero sera de tipo template. En este ultimo caso le asignamos un identificador C y como valor le pasaremos la clase anterior. Esto hara que las propiedades internas si no reciben un tipo de dato utilizara a esta clase para crear las propiedades y los tipos que manejaran seran los recibidos en los otros parametros. Ahora si, veamos un caso de la vida real:
#include <iostream>
#include <vector>
#include <deque>
#include <list>
template <class T, template<class, class...> class X, class... Args>
std::ostream& operator<<(std::ostream& os, const X<T, Args...> & o) {
os << __PRETTY_FUNCTION__ << ":" << std::endl;
for(auto const& obj : o) {
os << obj << ' ';
}
return os;
}
int main()
{
std::cout << "Vector: " << std::endl;
std::vector<float> x{ 3.14f, 4.2f, 7.9f, 8.08f };
std::cout << x << std::endl;
std::cout << "List: " << std::endl;
std::list<const char* > y{"tinchicus.com","es","mi","sitio"};
std::cout << y << std::endl;
std::cout << "Deque: " << std::endl;
std::deque<int> z{ 10,11,303,404 };
std::cout << z << std::endl;
return 0;
}
Primero definimos una serie de tipos que usaremos en el main. Luego tenemos un template con un parametro normal, seguido de un parametro template y otro de tipo variado. Este template lo aplicaremos en la sobrecarga del operador <<. En este recibiremos dos argumentos, el primero sera para el objeto que sobrecargaremos, por lo general es std::cout, y el segundo sera del tipo template y este a su vez recibira el tipo anterior y posterior al template. Lo primero que haremos sera acumular el resultado de la funcion __PRETTY_FUNCTION__ en el objeto establecido como argumento. Pero que es esta funcion? Es una macro que nos devuelve la funcion y sus distintos parametros, es muy similar a __FUNCTION__ y a su vez ambas son extensiones de la basica de C como es __func__, Pero la que usamos nos permite mostrar mejores a las funciones si tienen la opcion de PRETTY. Volviendo a la sobrecarga, tenemos un bucle donde pasaremos por todos los valores que recibamos tambien lo pasaremos a os pero separados por un espacio en blanco. Para finalmente mostrar todo lo introducido en os.
En el main, vamos a crear tres objetos del tipo vector, list y deque. En los tres casos, primero indicaremos cual es el tipo de la variable. Las definiremos a cada una con distintos valores y luego mostraremos los valores en cada variable y aqui se aplicara la sobrecarga que definimos anteriormente. Con todo esto comentado, compilemos y veamos como es la salida:
$ ./plantilla
Vector:
std::ostream& operator<<(std::ostream&, const X<T, Args ...>&) [with T = float; X = std::vector; Args = {std::allocator<float>}; std::ostream = std::basic_ostream<char>]:
3.14 4.2 7.9 8.08
List:
std::ostream& operator<<(std::ostream&, const X<T, Args ...>&) [with T = const char*; X = std::__cxx11::list; Args = {std::allocator<const char*>}; std::ostream = std::basic_ostream<char>]:
tinchicus.com es mi sitio
Deque:
std::ostream& operator<<(std::ostream&, const X<T, Args ...>&) [with T = int; X = std::deque; Args = {std::allocator<int>}; std::ostream = std::basic_ostream<char>]:
10 11 303 404
$
Observen cuanto detalle se nos muestra mediante el uso de la macro __PRETTY_FUNCTION__ donde podemos ver no solamente a la sobrecarga del operador sino que tambien vemos como detalla los argumentos que iremos utilizando. Luego veremos los valores de cada variable creada.
Nota:
Prueben de modificar __PRETTY_FUNCTION__ a __FUNCTION__ para que vean la diferencia.
Como pueden ver, usar este tipo de parametro nos puede ayudar a resumir algunos uso de parametros de template. Si vienen de los posts anteriores, se habran dado cuenta de las diferencias entres los tres tipo de parametros. Pero cada uno tiene sus pros y contras para cada situacion donde debemos implementarlos.
En resumen, hoy hemos visto a parametro template, que es, para que sirve, como se aplica, y un ejemplo para ver como trabaja. Espero les haya resultado de utilidad sigueme en tumblr, Twitter o Facebook para recibir una notificacion cada vez que subo un nuevo post en este blog, nos vemos en el proximo post.


Donatión
It’s for site maintenance, thanks!
$1.50
