- Encabezado de plantilla
- Principios generales de funcionamiento de la plantilla
- Plantillas frente a macros de preprocesador
- Características de tipos integrados y objetos en plantillas
- Plantillas de funciones
- Plantillas de tipos de objetos
- Plantillas de métodos
- Plantillas anidadas
- Ausencia de especialización de plantillas
Plantillas de funciones
Una plantilla de función consta de un encabezado con parámetros de plantilla (la sintaxis se ha descrito antes) y una definición de función en la que los parámetros de la plantilla denotan tipos arbitrarios.
Como primer ejemplo, considere la función Swap para intercambiar dos elementos de una array (TemplatesSorting.mq5). El parámetro de plantilla T se utiliza como tipo de la variable de array de entrada, así como el tipo de la variable local temp.
template<typename T>
|
Todas las sentencias y expresiones del cuerpo de la función deben ser aplicables a tipos reales, para los que se crearán seguidamente instancias de plantilla. En este caso, se utiliza el operador de asignación '='. Aunque siempre existe para los tipos integrados, puede ser necesario sobrecargarla explícitamente para los tipos definidos por el usuario.
El compilador genera por defecto la implementación del operador de copia para clases y estructuras, pero puede eliminarse implícita o explícitamente (véase la palabra clave delete). En particular, como vimos en la sección Conversión de tipos de objeto, tener un campo constante en una clase hace que el compilador elimine su opción de copia implícita. Seguidamente, la función Swap de la función anterior no puede utilizarse para objetos de esta clase: el compilador generará un error.
Para las clases/estructuras con las que trabaja la función Swap, es deseable tener no sólo un operador de asignación, sino también un constructor de copia, porque la declaración de la variable temp es en realidad una construcción con una inicialización, no una asignación. Con un constructor de copia, la primera línea de la función se ejecuta de una sola vez (se crea temp en base a array[i]), mientras que sin él, primero se llamará al constructor por defecto, y luego se ejecutará el operador '=' para temp.
Veamos cómo se puede utilizar la función de plantilla Swap en el algoritmo quicksort: lo implementa otra función de plantilla QuickSort.
template<typename T>
|
Observe que el parámetro T de la plantilla QuickSort especifica el tipo del parámetro de entrada array, y este array se pasa a la plantilla Swap. Así, la inferencia de tipo T para la plantilla QuickSort determinará automáticamente el tipo T para la plantilla Swap.
La función integrada ArraySize (como muchas otras) es capaz de funcionar con arrays de tipos arbitrarios: en cierto sentido, se trata también de una plantilla, aunque está implementada directamente en el terminal.
La ordenación se realiza gracias al operador de comparación '>' de la sentencia if. Como hemos señalado antes, este operador debe definirse para cualquier tipo T que se esté ordenando, ya que se aplica a los elementos de un array de tipo T.
Comprobemos cómo funciona la ordenación para arrays de tipos integrados.
void OnStart()
|
Dos llamadas a la función de plantilla QuickSort deducen automáticamente el tipo de T basándose en los tipos de los arrays pasados. Como resultado, obtendremos dos instancias de QuickSort para los tipos double y string.
Para comprobar la ordenación de un tipo personalizado, vamos a crear una estructura ABC con un campo entero x, y la rellenaremos con números aleatorios en el constructor. También es importante sobrecargar el operador '>' en la estructura.
struct ABC
|
Como los valores de la estructura se generan aleatoriamente, obtendremos resultados diferentes, pero siempre estarán ordenados de forma ascendente.
En este caso, el tipo T también se deduce automáticamente. Sin embargo, en algunos casos, la especificación explícita es la única forma de pasar un tipo a una plantilla de función. Así, si una función de plantilla debe devolver un valor de un tipo único (diferente de los tipos de sus parámetros) o si no hay parámetros, entonces sólo puede especificarse explícitamente.
Por ejemplo, la siguiente función de plantilla createInstance requiere que el tipo se especifique explícitamente en la instrucción de llamada, ya que no es posible «calcular» automáticamente el tipo T a partir del valor de retorno. Si no se hace así, el compilador genera un error de «desajuste de plantilla».
class Base
|
Si hay varios parámetros de plantilla, y el tipo del valor de retorno no está vinculado a ninguno de los parámetros de entrada de la función, entonces también es necesario especificar un tipo concreto al realizar una llamada:
template<typename T,typename U>
|
Tenga en cuenta que, si los tipos para la plantilla se especifican de forma explícita, entonces esto es necesario para todos los parámetros, aun cuando el segundo parámetro U podría inferirse a partir del argumento pasado.
Después de que el compilador haya generado todas las instancias de la función de plantilla, éstas participan en el procedimiento estándar para elegir la mejor candidata de todas las sobrecargas de funciones con el mismo nombre y el número adecuado de parámetros. De todas las opciones de sobrecarga (incluidas las instancias de plantilla creadas), se selecciona la más cercana en términos de tipos (con el menor número de conversiones).
Si una función de plantilla tiene algunos parámetros de entrada de tipos específicos, sólo se considerará candidata si estos tipos coinciden completamente con los argumentos: cualquier necesidad de conversión hará que la plantilla sea «descartada» como inadecuada.
Las sobrecargas sin plantilla tienen prioridad sobre las sobrecargas con plantilla, las más especializadas («de enfoque estrecho») «ganan» frente a las sobrecargas con plantilla.
Si se especifica explícitamente el argumento de plantilla (tipo), se aplicarán las reglas para la conversión implícita de tipos para el argumento de función correspondiente (valor pasado), si es necesario, si estos tipos difieren.
Si varias variantes de una función coinciden por igual, obtendremos un error de «llamada ambigua a una función sobrecargada con los mismos parámetros».
Por ejemplo, si además de la plantilla MyCast se define una función para convertir una cadena en un tipo booleano:
bool MyCast(const string u)
|
entonces, al llamar a MyCast<double,string>("123.0") se producirá el error indicado, ya que las dos funciones sólo difieren en el valor de retorno:
'MyCast<double,string>' - ambiguous call to overloaded function with the same parameters
|
A la hora de describir funciones de plantilla se recomienda incluir todos los parámetros de la plantilla en los parámetros de la función. Los tipos sólo pueden deducirse de los argumentos, no del valor de retorno.
Si una función tiene un parámetro de tipo de plantilla T con un valor por defecto, y el argumento correspondiente se omite al llamarlo, entonces el compilador también fallará al inferir el tipo de T y mostrará un error de «no se puede aplicar plantilla».
class Base
|