Sobrecarga de funciones
MQL5 permite definir funciones con el mismo nombre pero con distinto número o tipo de parámetros en el mismo código fuente. Este enfoque se denomina sobrecarga de funciones y suele aplicarse cuando una misma acción puede ser desencadenada por diferentes entradas. Las diferencias en las firmas permiten al compilador determinar automáticamente a qué función llamar en función de los argumentos pasados. Pero hay algunas particularidades.
Las funciones no pueden diferir únicamente en cuanto a tipo de devolución. En este caso, el mecanismo de sobrecarga no se activa y se devuelve el error «la función ya se ha definido y tiene un tipo diferente».
Si funciones del mismo nombre tienen distinto número de parámetros y los parámetros «extra» se declaran opcionales, el compilador no podrá determinar a cuál de ellas llamar. Esto generará el error «llamada ambigua a función sobrecargada con los mismos parámetros».
Cuando se llama a una función sobrecargada, el compilador hace coincidir los argumentos y parámetros en las sobrecargas disponibles. Si no se encuentra una coincidencia exacta, el compilador intenta añadir o eliminar el modificador const y realizar la expansión de tipos numéricos y la conversión aritmética. En el caso de los punteros a objetos se utilizan reglas de herencia de clases.
Con un número diferente de parámetros o tipos de parámetros no relacionados en la misma posición (como un número y una cadena), la elección suele estar clara. Sin embargo, si los tipos de parámetros deben convertirse implícitamente de uno a otro, puede surgir la ambigüedad.
Por ejemplo, tenemos dos funciones de suma:
double sum(double v1, double v2)
|
Entonces, la siguiente llamada dará lugar a un error:
sum(1, 3.14); // overloaded function call is ambiguous |
Aquí, el compilador se siente igual de incómodo con cada una de las sobrecargas: para la función double sum(double v1, double v2) es necesario convertir implícitamente el primer argumento en double, y para int sum(int v1, int v2) es necesario convertir el segundo argumento en int.
El término 'sobrecarga' debe interpretarse en el sentido de que un nombre reutilizado se «carga» con «deberes» varias veces más pesados que un nombre normal utilizado sólo para una función.
Intentemos sobrecargar la función para la transposición de matrices. Ya teníamos un ejemplo para un array de 2x2 (véase Parámetros de valor y parámetros de referencia). Vamos a realizar la misma operación para un array de 3x3. El tamaño de un parámetro de array multidimensional en dimensiones superiores (distintas de cero) cambia el tipo, es decir, doble [][2] es diferente de doble [][3]. Por lo tanto, sobrecargaremos la versión antigua de la función:
void Transpose(double &m[][2]); |
Añadiendo uno nuevo (FuncOverload.mq5):
void Transpose(double &m[][3]); |
En la implementación de la nueva versión es conveniente utilizar la función de ayuda Swap para intercambiar dos elementos de matriz en los índices dados.
void Transpose(double &m[][3])
|
Ahora podemos llamar a ambas funciones desde OnStart utilizando la misma notación para arrays de diferentes tamaños. El propio compilador generará una llamada a las versiones correctas.
double a[2][2] = {{1, 2}, {3, 4}};
|
Es importante señalar que el modificador const del parámetro, aunque cambia el prototipo de la función, no siempre es una diferencia suficiente para la sobrecarga. Dos funciones del mismo nombre, que sólo difieren en la presencia y ausencia de const para algún parámetro, pueden considerarse iguales. Esto provocará un error del tipo «la función ya está definida y tiene cuerpo». Este comportamiento se produce porque, para los parámetros de valor, el modificador const se descarta cuando se asigna el argumento (porque un parámetro de valor, por definición, no puede cambiar el argumento en el código de llamada), y esto no permite seleccionar una de las varias funciones solapadas basándose en él.
Para demostrarlo, basta con añadir una función en el script:
void Swap(double &m[][3], int i, int j); |
Se trata de una sobrecarga infructuosa de la ya existente:
void Swap(double &m[][3], const int i, const int j); |
La única diferencia entre las dos funciones son los modificadores const para los parámetros i y j . Por lo tanto, ambos son adecuados para llamar con argumentos del tipo int y pasar por valor.
Cuando los parámetros se pasan por referencia, la sobrecarga con una diferencia de sólo atributos const/non-const tiene éxito porque, para las referencias, el modificador const es importante (cambia el tipo y elimina la posibilidad de conversión implícita). Esto se demuestra en el script con un par de funciones:
void SwapByReference(double &m[][3], int &i, int &j)
|
Se dejan como stubs casi vacíos, en los que se imprime la firma de cada función mediante la llamada Print(__FUNCSI__). Esto permite garantizar que se llame a la versión adecuada de la función en función del atributo const de los argumentos.