Inicio > Apunte, C++, WIN-32 > Especialización parcial de plantillas

Especialización parcial de plantillas


Un tema que me gustaría abordar es el de la especialización parcial de plantillas. Es algo sencillo, pero si no se tiene en cuenta, se puede caer en errores al leer un código. Consideremos el siguiente código:

template<class T>
T suma(const T& x, const T&amp; y)
{
  return x + y;
}

 

Evidentemente la función toma dos valores del mismo tipo y regresa la suma. Evidentemente el tipo de dato debe tener sobrecargado el operador de adición. Así, las siguientes llamadas funcionan de maravilla.

int i = 5, j = 4;
float f = 4.2, g = 3.7;

cout << suma(i, j) << endl;
cout << suma(f, g) << endl;

 

Sabemos de antemano que el compilador creará dos versiones de la función suma con el tipo de dato entero y otro para un número de punto flotante. Ahora supongamos que queremos pasarle como parámetros dos cadenas de texto. Y queremos -por supuesto- que la semántica sea diferente: en lugar de regresarnos la suma de las dos cadenas (lo cuál de antemano sería imposible ya que no hay un operador de adición sobrecargado) nos regrese la concatenación de la primera y la segunda cadena. ¿Cómo le hacemos?

Bueno, empleamos la especialización parcial de la plantilla. Es decir, basados en el concepto de que el compilador crea una versión de la plantilla para cada tipo de dato empleado, nos adelantamos y le decimos al compilador que cuando los parámetros sean char*, se comporte de diferente forma.

template<>
T suma<char*>(const T x, const T y)
{
  strcat(x, y);
  return x;
}

 

Lo anterior es la llamada especialización parcial. Nos adelantamos y le decimos al compilador que con char* no se meta, eso lo arreglamos nosotros. Por supuesto, el compilador hará su trabajo con respecto a los otros tipos de dato.

Por supuesto, lo anterior es cierto también para las clases. Más aún, en una clase podemos hacer la especialización parcial a nivel de toda la clase o sólo de una función. Incluso, podemos hacer cosas como lo siguiente:

template<class T>
class C
{
  public:
      static const bool m_bEsEspecializada = false;
};

template<>
class C<char>
{
  public:
      static const bool m_bEsEspecializada = true;
};

void foo()
{
  C<bool> c1;
  C<char> c2;

  cout << boolalpha;
  cout << c1.m_bEsEspecializada << endl;
  cout << c2.m_bEsEspecializada << endl;
}

 

Por supuesto, el primer valor mostrado en pantalla sería false mientras que el segundo sería true.

La especialización parcial de plantillas es muy potente ya que nos permite crear versiones diferentes para diferentes tipos de datos. Es, digámoslo de alguna manera, una forma de sobrecarga de funciones y clases a nivel de metaprogramación. Pero por supuesto sin emplear herencia y a niveles más amplios: el polimorfismo no nos deja cambiar valores de variables y además aplica a todo tipo de dato.

La especialización parcial de plantillas se emplea en muchos lugares. Por ejemplo, la clase std::numeric_limits la emplea para establecer las propiedades de los diferentes tipos de datos numéricos. Además, se emplea bastante en metaprogramación, como el concepto desarrollado por Andrei Alexandrescu de "clases basadas en políticas", tema que trataré más adelante.

Por supuesto, no deberíamos abusar del concepto, ya que cada especialización hace que se genere una nueva "versión" de la función o de la clase, y pues esto aumenta el tamaño de nuestro binario final.

Ya por último, dejo el código de un programilla que utiliza este concepto para realizar sumas de objetos (enteros, reales, etc.) y que cuando el tipo de dato es una cadena de texto (char*) entonces se devuelva una cadena concatenada.


#include <cstring>
#include <string>
#include <iostream>

using namespace std;

template<class T>
class operacion
{
    public:
        T suma(const T& t1, const T& t2)
        {
            return t1 + t2;
        }
};

template<>
class operacion<char*>
{
    public:
        char* suma(char* t1, char* t2)
        {
            return strcat(t1, t2);
        }
};

int wmain(int argc, wchar_t* argv[])
{
    operacion<int> op_int;
    int rs_int = op_int.suma(10, 5);
    cout << "Sumando enteros: 10 + 5 = " << rs_int << endl;

    operacion<float> op_flt;
    float rs_flt = op_flt.suma(1.8F, 7.4F);;
    cout << "Sumando reales: 1.8 + 7.4 = " << rs_flt << endl;

    char hola[50] = "Hola ";
    operacion<char*> op_str;
    char* res_sz = op_str.suma(hola, "Mundo");
    cout << "Sumando cadenas de texto: Hola + Mundo = " << res_sz << endl;

    cout << "Terminando..." << endl;
    cin >> string();

    return 0;
}


Categorías:Apunte, C++, WIN-32 Etiquetas: ,
  1. fran
    marzo 29, 2012 a las 3:41 am

    gracias, muy interesante

  1. No trackbacks yet.

Responder

Introduce tus datos o haz clic en un icono para iniciar sesión:

Logo de WordPress.com

Estás comentando usando tu cuenta de WordPress.com. Cerrar sesión / Cambiar )

Imagen de Twitter

Estás comentando usando tu cuenta de Twitter. Cerrar sesión / Cambiar )

Foto de Facebook

Estás comentando usando tu cuenta de Facebook. Cerrar sesión / Cambiar )

Google+ photo

Estás comentando usando tu cuenta de Google+. Cerrar sesión / Cambiar )

Conectando a %s