Un ejemplo típico de ésto (poco útil, pero muy simple), es la siguiente función:
int min(int a, int b)
{
if ( a < b )
return a;
else return b;
}
Resulta frustrante que esta función sólo sirva para números enteros, pues lo único que realmente debería cambiar para que fuese aceptada es la cabecera. Con esta idea en mente, crearemos la primera plantilla:
template<typename T>
T min(T a, T b)
{
if ( a < b )
return a;
else return b;
}
typename es la palabra clave recomendada para poner al lado de template, si bien hasta hace algunos años se utilizaba class:
template<class T>
T min(T a, T b)
{
if ( a < b )
return a;
else return b;
}
Lo mejor de todo es que, hasta que no la utilicemos, la plantilla no ocupa espacio en memoria (¡quitando el espacio necesario en el código fuente, claro!). En el momento de utilizarla, el compilador crea el código necesario sustituyendo las T's por los tipos necesarios:
int main()
{
cout << min( 7, 8 ) << endl;
cout << min( string( "hola" ), string( "adiós") ) << endl;
}
Así, en el código final habrá dos códigos ejecutables creados a partir de la plantilla (de ahí, su nombre) para la función min(): en uno se ha sustituído la T por int, mientras que en el otro, por la clase string.
Nótese que los objetos con los que utilicemos la plantilla para min() deben tener el operador < sobrecargado, ya que de otra forma daría un error al compilar.
class Persona {
private:
string nombre;
public:
Persona(const string &n) : nombre( n )
{}
const string &getNombre() const
{ return nombre; }
};
int main()
{
Persona p1( "pablito" );
Persona p2( "pepito" );
cout << min( p1, p2 ) << endl; // Error, Persona no tiene operador <
}
Si se sobrecarga el operador <, tendremos entonces un programa que compila y ejecuta:
class Persona {
private:
string nombre;
public:
Persona(const string &n) : nombre( n )
{}
const string &getNombre() const
{ return nombre; }
bool operator<(const Persona &p)
{ return ( this->getNombre() < p.getNombre() ); }
};
int main()
{
Persona p1( "pablito" );
Persona p2( "pepito" );
cout << min( p1, p2 ) << endl; // Correcta, Persona tiene operador <
}
Pensando en una mayor eficiencia, es posible realizar un cambio en nuestra plantilla que no afectará a su uso, pero sí a su consumo de recursos:
template<typename T>
const T &min(const T &a, const T &b)
{
if ( a < b )
return a;
else return b;
}
Estamos cambiando el paso de parámetros (y la devolución de valores) de copia a referencia, de manera que sea un int lo que le vayamos a pasar, o un objeto de la clase Persona, la función sea igual de eficiente.
Por último, es de destacar que ponemos dos T's en la lista de parámetros de nuestra fución min(). Entre otras cosas (ya discutidas), esto también obliga a que los tipos de ambos parámetros sean el mismo. Así ...
int main()
{
cout << min( 7, string( "hola" ) ) << endl;
}
Nos presentaría un error al compilar, indicando que no es posible crear el código a partir de la plantilla, pues esta exige que los tipos de los parámetros sean iguales, no un int y una cadena.