
Del básico al intermedio: Plantilla y Typename (II)
Introducción
En el artículo anterior, "Del básico al intermedio: Plantilla y Typename (I)", comenzamos a hablar de un tema bastante complejo, pero muy divertido e interesante: la creación de plantillas de funciones y procedimientos. Ya que este es, de hecho, un tema bastante complicado de abordar y explicar en pocos artículos, lo dividiremos en varios artículos. Sin embargo, no me profundizaré en exceso solo en este tema, explorándolo a fondo antes de pasar a hablar de otros igualmente interesantes, ya que existen algunas cosas que solo tendrán sentido si también se abordan otros temas.
Sin embargo, trabajaremos durante un tiempo solo en este tema, hasta que hayamos construido una base suficientemente sólida y amplia para poder entrar en otros temas antes de volver a hablar de plantillas. Pues, de hecho, este es un tema muy amplio y vasto, como tú verás en los próximos artículos. De cualquier manera, es necesario explicar algunos puntos que no mencioné en el artículo anterior, precisamente para no hacerlo demasiado complicado y aterrador. Quiero que tú, querido lector, te sientas a gusto y cómodo al estudiar cada uno de estos artículos, y que ellos te ayuden a comenzar a programar de forma más correcta y segura, teniendo al menos una buena noción de cada herramienta disponible en MQL5.
Aunque gran parte de lo que abordamos aquí también sea aplicable a otros lenguajes, es necesario hacer las debidas consideraciones para aplicar los conceptos de la manera adecuada.
Vale, ahora que tú conoces tales palabras, para que podamos posicionarnos respecto a lo que trataremos aquí, llegó la hora de relajarse, remover posibles distracciones a tu alrededor y enfocarse en lo que será explicado en este artículo. Aquí, trataremos un poco más sobre plantillas.
El contenido expuesto aquí tiene como objetivo la didáctica. En ningún caso debe ser considerado como una aplicación final, donde el objetivo no sea el estudio de los conceptos presentados.
Plantillas y más plantillas
Una de las cosas más interesantes respecto a las plantillas es que, si están bien planificadas, se convierten en una herramienta indispensable. Esto se debe a que tú terminas creando lo que muchos podrían llamar un modelo de implementación rápida. Es decir, tú dejas de necesitar programar todo y pasas a programar solo parte de los detalles necesarios.
Muy probablemente tú, mi querido y estimado lector, no tienes la menor idea de lo que estoy hablando en este momento. Pero, conforme vayas estudiando y adquiriendo práctica como programador, notarás que muchas cosas pueden hacerse de manera mucho más rápida si se adoptan ciertas medidas. Saber y entender cada herramienta a nuestra disposición marca toda la diferencia en el momento de escoger el mejor camino. uevamente, NO TE APEGUES A LOS NOMBRES. Procura entender el concepto adoptado y, con el tiempo, tú conseguirás tomar las riendas y construir tu propio camino.
Entonces, comencemos con una implementación bien simple, basada en lo que se vio en el artículo anterior. Está justo aquí abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(Sum(10, 25)); 07. Print(Sum(-10.0, 25.0)); 08. } 09. //+------------------------------------------------------------------+ 10. template <typename T> 11. T Sum(T arg1, T arg2) 12. { 13. Print(__FUNCTION__, "::", __LINE__); 14. return arg1 + arg2; 15. } 16. //+------------------------------------------------------------------+
Código 01
En este código 01, explicado en el artículo anterior, tenemos la capacidad de utilizar la misma función para trabajar con tipos diferentes de datos. Sin embargo, hay algo aquí que termina volviendo las cosas algo molestas, por no decir otra cosa. El problema es el siguiente: observa que, en las líneas seis y siete, tenemos datos de tipos diferentes. Como tú debes haber notado, si estudiaste el contenido del artículo anterior, el compilador puede lidiar con esto perfectamente bien, creando las funciones sobrecargadas de modo que, al final, no es necesario definir qué tipo de dato está siendo utilizado. No obstante, y aquí es donde se encuentra la parte molesta, los tipos de datos utilizados aquí necesitan ser iguales. Es decir, si el primer argumento pasado para la plantilla de la función Sum es del tipo float, el segundo argumento también NECESITA ser del tipo float. De lo contrario, el compilador disparará un error o, en el mejor de los casos, una advertencia indicando que existe un problema entre los tipos de datos utilizados.
Para entender mejor, vamos a modificar una de las líneas. Puede ser la línea seis o siete de este código 01, de manera que se pasarán tipos diferentes a la función. Recuerda que la función de hecho aún no existe. El compilador necesita construirla con base en la plantilla proporcionada. Así siendo, el código 01 fue modificado conforme se muestra a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(Sum(10, 25)); 07. Print(Sum(5, 35.2)); 08. } 09. //+------------------------------------------------------------------+ 10. template <typename T> T Sum(T arg1, T arg2) 11. { 12. Print(__FUNCTION__, "::", __LINE__); 13. return arg1 + arg2; 14. } 15. //+------------------------------------------------------------------+
Código 02
Nota que cambié solo los valores que estaban siendo pasados a la función. En este caso, decidí alterar la línea siete, como tú puedes observar al comparar el código 01 con el código 02. Sin embargo, a pesar de ese pequeño e inocente cambio en el código, observa lo que resultó cuando intentamos compilarlo.
Imagen 01
Esos mensajes vistos en la imagen 01 pueden variar ligeramente, dependiendo del caso específico. Tú puedes acabar perdiéndote al notar que el compilador no logró establecer una base de enlace, siendo que el único cambio realizado fue en los valores presentes en la línea siete del código. Esta es precisamente la parte molesta a la que yo me estaba refiriendo. Como nuestro ejemplo es puramente didáctico, tú puedes pensar: "Esto no tiene sentido." ¿Por qué el compilador no está logrando crear el código con base en la plantilla proporcionada? El motivo es que, debido a que el primer argumento es un valor entero, no podemos colocar un valor de punto flotante en el segundo argumento. O viceversa, donde primero colocamos un valor del tipo punto flotante y después uno entero.
Como la plantilla de la función Sum está siendo definida en el código 02, el compilador no consigue establecer una función adecuada para efectuar lo que se espera de la función plantilla, es decir, la función Sum. Muchos principiantes acaban desistiendo y optando por otro enfoque del problema, cuando todo podría resolverse de una manera muy simple. Sin embargo, para resolver este tipo de cuestión de forma adecuada, es necesario primero entender qué se espera de la función o procedimiento utilizado como plantilla, con el fin de crear otros procedimientos o funciones sobrecargadas.
Como aquí el objetivo es puramente didáctico, la función que estamos usando es bien simple y directa. Todo lo que esperamos de ella es que sume dos valores y nos devuelva el resultado de esta suma. Entonces, si tú ya intentaste sumar un valor del tipo punto flotante con un valor entero, sabes que el resultado es un valor del tipo punto flotante.
Este tipo de cosa es conocido como conversión de tipo, o typecasting, y ya hablamos sobre él cuando abordamos variables y constantes. Básicamente, todo se resume a lo que se muestra en la imagen a continuación.
Imagen 02
Pues bien, con base en lo que se muestra en esta imagen 02, sabemos que el tipo double es utilizado en todos los casos en los que se realizan operaciones matemáticas con tipos diferentes, como está siendo hecho en la línea siete del código 02. Sabiendo esto y teniendo la noción de que el compilador no está usando una función sobrecargada, sino una plantilla, podemos forzar al compilador a entender que estamos al tanto de lo que está ocurriendo. Así, podemos modificar el código 02 para que realmente funcione, como puede verse en la implementación justo a continuación.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(Sum(10, 25)); 07. Print(Sum((double) 5, 35.2)); 08. } 09. //+------------------------------------------------------------------+ 10. template <typename T> T Sum(T arg1, T arg2) 11. { 12. Print(__FUNCTION__, "::", __LINE__); 13. return arg1 + arg2; 14. } 15. //+------------------------------------------------------------------+
Código 03
Observa que, en este código 03, estamos utilizando el mismo código visto en el código 02. Sin embargo, aquí estamos forzando al compilador a entender que estamos al tanto de que estamos lidiando con un tipo double, aunque el valor sea una variable del tipo entero. Esta conversión explícita de tipo, hecha al añadir el término de la línea siete del código 03, diferencia esta misma línea siete del código 02 y hace posible que el compilador utilice la función plantilla Sum de forma que cree una sobrecarga adecuada. Con esto, el resultado final puede observarse justo abajo.
Imagen 03
El detalle es que esta solución que estoy presentando no es la única posible. Podemos hacer algo ligeramente diferente y obtener el mismo resultado. Todo es una cuestión de entender lo que queremos implementar de hecho y cuáles son nuestras necesidades en ese momento. Por tanto, más que entender o simplemente memorizar un código, o peor aún, usar la vieja táctica de copiar y pegar (CTRL+C y CTRL+V), necesitamos comprender realmente los conceptos utilizados. Solo así conseguiremos resolver cualquier problema relacionado con la programación cuando este surja.
Otra manera de resolver este mismo problema es restringir uno de los argumentos a un tipo específico. Aunque muchos no lo consideren apropiado, este método puede resolver varios tipos de problemas en situaciones muy específicas, en las cuales sabemos de antemano que siempre utilizaremos un cierto tipo de información en un argumento específico.
Así, suponiendo que el primer argumento siempre, y SIEMPRE, será del tipo entero, podemos hacer algo como lo mostrado justo abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. void OnStart(void) 05. { 06. Print(Sum(10, 25)); 07. Print(Sum(5, 35.2)); 08. } 09. //+------------------------------------------------------------------+ 10. template <typename T> T Sum(short arg1, T arg2) 11. { 12. Print(__FUNCTION__, "::", __LINE__); 13. return arg1 + arg2; 14. } 15. //+------------------------------------------------------------------+
Código 04
El resultado de este código es el mismo del código 03, es decir, la imagen 03. Sin embargo, en el código 04, estamos informando al compilador que uno de los argumentos de la función plantilla deberá ser siempre del tipo entero. En este caso, estamos usando un tipo de 16 bits con signo, pero podría ser cualquier otro. Observa que, en esta situación específica, no necesitamos hacer una conversión de tipo de manera explícita. Esto se debe a que el compilador ya sabrá cómo manejar la situación. Sin embargo, es importante subrayar que el segundo argumento será definido por el compilador durante la fase de creación del ejecutable. Por esta razón, tendremos una función para responder a la llamada de la línea seis, donde utilizamos solo tipos enteros, y otra función solamente para responder a la línea siete, donde estamos utilizando un tipo entero y un punto flotante.
Ahora te pregunto: ¿este tipo de cosa no es muy divertido para jugar y practicar? Observa que estamos instruyendo al compilador sobre cómo debe trabajar. Y, al hacer eso, conseguimos, sin mucho esfuerzo, crear diversas posibilidades de construir una misma cosa.
Pero no pienses que se ha terminado. Aún hay otra manera de resolver este mismo tipo de problema. En este caso, sin embargo, la cosa acaba volviéndose un poco más complicada después, trayendo otro tipo de problema que explicaré cómo manejar en otro momento. Aun así, es algo que realmente merece ser mostrado, ya que encaja en diversas situaciones, pudiendo incluso abrir las puertas a un nuevo mundo de posibilidades de uso y formas de trabajar con MQL5.
Sin embargo, para evitar que tú termines confundiendo las cosas, veremos eso en otro tema. Primero, estudia lo que se ha visto hasta aquí, practica y asimila estos conceptos para, solo después, aventurarte en lo que será visto a continuación.
Una plantilla, múltiples tipos
En el tema anterior, vimos cómo lidiar con algo bastante molesto, que a veces nos limita en términos de utilización de una plantilla. Sin embargo, lo que se vio allí es solo la primera parte de todo lo que realmente podemos hacer. Lo que mostraré aquí no es algo muy común, al menos no aquí en MQL5, ya que, hasta el momento, no recuerdo haber visto a nadie utilizando tal metodología. Entonces, muchos pueden pensar que tal posibilidad no existe o que no puede ser implementada, quedando así de manos atadas, sin poder alcanzar su objetivo primario.
Sin embargo, el hecho de que tú no hayas visto o escuchado relatos sobre el uso de algo no significa que ese algo no exista o no sea aceptado por el lenguaje de programación. Muchas veces, el problema ni siquiera es ese, sino otros tipos de problemas que pueden llegar a ocurrir, precisamente por estar haciendo un uso incorrecto o, como ocurre en gran parte de los casos, por no haber comprendido realmente algunos conceptos referentes a alguna herramienta de programación.
En el artículo anterior, expliqué que ese T utilizado en una plantilla es, en realidad, un identificador que será utilizado por el compilador para identificar localmente un determinado tipo. Esto es necesario para saber cómo lidiar con la información que estará llegando. Si tú entiendes el concepto de identificador, sabes que, si un identificador está correctamente declarado, se comportará como una variable o como una constante.
En el caso del identificador, que es ese tal T que vemos en la línea diez del código 04, NO ES UNA VARIABLE, ya que no podrá ser alterado después de ser definido. Entonces, claramente, es una constante. Sin embargo, es una constante definida por el compilador que representa el tipo de dato esperado.
Presta atención, querido lector: cuando me refiero al tipo de dato, me estoy refiriendo al contenido visto en la imagen 02. Por eso, es importante comprender el concepto, no memorizar fórmulas o modelos de implementación. Comprendido este concepto, que, aunque es simple, pronto notarás que es bastante poderoso, podemos crear una solución casi mágica, que podrá entender cómo una plantilla deberá ser utilizada para crear algo, ya sea una función o un procedimiento. Para ello, necesitaremos añadir tantos identificadores como sean necesarios para cubrir la mayor cantidad posible de casos permitidos. Esta decisión será tuya, querido lector, durante la fase de implementación del código.
Entonces, podemos modificar el código 04 y buscar algo más parecido con el código 02, pero sin el problema encontrado en el código 02, en el cual habría solo un tipo de dato que podría ser utilizado. Parece complicado, pero es mucho más simple de lo que uno puede imaginar. A continuación, veremos cómo sería la solución del problema aplicando el nuevo concepto de utilización de múltiples identificadores de tipo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define PrintX(X) Print(#X, " => ", X, "\n"); 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. PrintX(Sum(10, 25)); 09. PrintX(Sum(5, 35.2)); 10. } 11. //+------------------------------------------------------------------+ 12. template <typename T1, typename T2> T1 Sum(T1 arg1, T2 arg2) 13. { 14. Print(__FUNCTION__, "::", __LINE__); 15. 16. return (T1)(arg1 + arg2); 17. } 18. //+------------------------------------------------------------------+
Código 05
Aquí es donde, como dicen, la cosa empieza a complicarse de verdad. Esto se debe a que las cosas comienzan, de hecho, a volverse bastante más complicadas y confusas, aunque yo esté siendo gentil y mostrando las cosas poco a poco. Sin embargo, el problema aquí está precisamente en lo que estamos haciendo en la línea doce, donde se hace la declaración de la plantilla de la función Sum.
Cuando se ejecuta, este código 05 producirá el resultado visto en la imagen justo abajo.
Imagen 04
Muy bien, estoy destacando un punto en esta imagen precisamente para llamar tu atención, querido lector. Observa que el resultado de la operación está equivocado. O mejor dicho, NO CORRESPONDE al valor esperado, ya que el valor esperado aquí sería el mismo que podemos visualizar en la imagen 03. ¿Pero por qué? Bueno, podrías imaginar que es la forma en que está escrita la línea 12, ya que eso aparentemente no tiene el menor sentido. Sin embargo, el problema NO ESTÁ EN LA LÍNEA DOCE, como se podría suponer, sino en la línea 9 o en la línea 16, dependiendo de la forma en que tú analices el código o de la manera en que fue pensado para ser implementado. Por eso, es importante entender los conceptos adoptados y siempre pensar antes de salir escribiendo código a lo loco, como si fueras un desquiciado desbocado.
Tal vez no me creas. Entonces, NO VAMOS A TOCAR LA LÍNEA DIECISÉIS, sino la línea nueve, alterando el orden en que los valores están siendo declarados. Esto nos lleva al código presentado justo abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define PrintX(X) Print(#X, " => ", X, "\n"); 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. PrintX(Sum(10, 25)); 09. PrintX(Sum(35.2, 5)); 10. } 11. //+------------------------------------------------------------------+ 12. template <typename T1, typename T2> T1 Sum(T1 arg1, T2 arg2) 13. { 14. Print(__FUNCTION__, "::", __LINE__); 15. 16. return (T1)(arg1 + arg2); 17. } 18. //+------------------------------------------------------------------+
Código 06
Observa que solo cambiamos lo que se dijo, y el resultado al ejecutar el código es lo que podemos ver justo abajo.
Imagen 05
¡Madre Santísima! ¡Por DIOS, qué cosa más loca y sin sentido! ¡Esto no es cosa de DIOS! ¡Esto es cosa del DEMONIO! Ahora sí que me dio miedo meterme con programación antes de entender mejor el asunto. Yo pensaba que todo era muy simple y fácil. Incluso ya me consideraba un programador, pues conseguía escribir pequeños fragmentos de código que estaban funcionando. Pero, viendo esto, me doy cuenta de que aún no sé absolutamente nada. Apenas estoy comenzando a aprender lo que significa ser un programador de verdad.
Calma, mi querido lector, no es para tanto. De hecho, las cosas no siempre son tan simples como parecen a primera vista, especialmente cuando entramos en una zona de confort y no salimos de ahí. Pero el problema es que mucha gente se considera un programador de calidad y, por eso, simplemente deja de estudiar, quedando así paralizado. Pues te afirmo:
Un buen programador NUNCA DEJA DE ESTUDIAR. Siempre está actualizándose y estudiando nuevos conceptos. SIEMPRE.
Este tipo de cosa que tú acabas de ver suceder aquí, de hecho, DESTRUYE la moral de un programador, aún más cuando le cae en las manos un código que, en principio, está correcto. Y, de hecho, está correcto. Sin embargo, el mismo siempre devuelve resultados erróneos, sin motivo aparente. Por eso, no te ilusiones con la idea de que basta saber escribir algunos códigos para ser considerado un programador. Para llegar a ese nivel, tú necesitas pasar por muchas cosas, y muchas de ellas solo el tiempo podrá enseñártelas. Yo ya pasé por una situación semejante y te digo: me costó mucho tiempo aprender a resolverla. Llegué a este punto de entender por qué un código funcionaba en un momento y, en otro, se volvía completamente loco, dando resultados aparentemente sin ningún sentido.
Pero vamos a entender lo que está ocurriendo aquí, tanto en el código 05 como en el código 06, pues son la misma cosa, siendo la única diferencia este simple cambio en la secuencia de declaración de los parámetros de la línea nueve.
Cuando el compilador encuentra una llamada a la plantilla que estamos declarando en la línea doce, verifica qué tipo de dato está utilizando cada argumento, de la misma forma que se hacía antes, creando así una función sobrecargada para atender esa llamada específica, en caso de que ninguna función creada antes logre atender al modelo actual.
Como en la línea doce tenemos dos typename para capturar dos tipos diferentes, podemos utilizar dos tipos de datos completamente distintos, lo que no era posible antes. Esto cubre perfectamente bien una gran cantidad de casos, ya que, básicamente, los valores pueden ser enteros o del tipo punto flotante, sin que tengamos que preocuparnos por eso al principio. Sin embargo, por cuestiones específicas, esta modelación no cubre todos los casos, pero eso no viene al caso. Sabiendo que podemos utilizar datos del tipo entero y del tipo punto flotante simultáneamente y sin problemas, podemos tener una plantilla casi perfecta.
Así, el tipo de dato declarado en el primer argumento será colocado en la constante T1 por el compilador. Ya el tipo de dato usado en el segundo argumento será colocado en la constante T2. Entonces, al hacer la conversión de tipo en la línea dieciséis para corresponder al tipo de retorno de la función Sum, podremos obtener un resultado como el mostrado en las imágenes 04 o 05.
Como solo mirando el código de la plantilla tenemos una visión vaga de lo que el compilador construirá de hecho, resulta difícil entender por qué tenemos resultados tan diferentes solo por la simple modificación en el orden de declaración de los valores en la línea nueve.
Para facilitar la comprensión, veremos cuál sería la función escrita por el compilador en ambos casos. Entonces, suponiendo que no estuviésemos utilizando plantilla sino una codificación tradicional, tendríamos, como código 05, lo que puede verse justo abajo.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define PrintX(X) Print(#X, " => ", X, "\n"); 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. PrintX(Sum(10, 25)); 09. PrintX(Sum(5, 35.2)); 10. } 11. //+------------------------------------------------------------------+ 12. int Sum(int arg1, int arg2) 13. { 14. Print(__FUNCTION__, "::", __LINE__); 15. 16. return (int)(arg1 + arg2); 17. } 18. //+------------------------------------------------------------------+ 19. int Sum(int arg1, double arg2) 20. { 21. Print(__FUNCTION__, "::", __LINE__); 22. 23. return (int)(arg1 + arg2); 24. } 25. //+------------------------------------------------------------------+
Código 07
Este código 07, cuando se ejecuta, producirá exactamente lo que se muestra en la imagen 04. Ya el código 06, si fuese producido en un modelo de programación tradicional, tendría lo que puede verse justo a continuación como contenido interno.
01. //+------------------------------------------------------------------+ 02. #property copyright "Daniel Jose" 03. //+------------------------------------------------------------------+ 04. #define PrintX(X) Print(#X, " => ", X, "\n"); 05. //+------------------------------------------------------------------+ 06. void OnStart(void) 07. { 08. PrintX(Sum(10, 25)); 09. PrintX(Sum(35.2, 5)); 10. } 11. //+------------------------------------------------------------------+ 12. int Sum(int arg1, int arg2) 13. { 14. Print(__FUNCTION__, "::", __LINE__); 15. 16. return (int)(arg1 + arg2); 17. } 18. //+------------------------------------------------------------------+ 19. double Sum(double arg1, int arg2) 20. { 21. Print(__FUNCTION__, "::", __LINE__); 22. 23. return (double)(arg1 + arg2); 24. } 25. //+------------------------------------------------------------------+
Código 08
De la misma manera que el código 07 daría origen a la imagen 04, este código 08 daría origen a la imagen 05. Ahora, nota que la diferencia entre un código y otro es muy sutil, pudiendo pasar desapercibida en diversas situaciones, sin que siquiera nos demos cuenta de que algo está mal. Sin embargo, a diferencia del código que hace uso de plantillas, en estos códigos 07 y 08 tú identificarías rápidamente la causa del problema. Al observar los resultados, notarías que existe una conversión explícita de tipo, que estaría forzando al código a presentar una respuesta errónea, corrigiendo entonces el problema.
Sin embargo, lo mismo no sería fácilmente perceptible cuando se estuviese utilizando la plantilla. En este caso, tú tendrías dificultad, sufrirías e incluso podrías desistir de continuar por ese camino. Y, si insistieras bastante, en algún momento, tú acabarías notando dónde estaría el error. Sin embargo, antes de que tú pienses: "Bien, todo lo que necesitamos hacer es utilizar una conversión de tipo explícita al tipo double en la línea dieciséis del código 05 o del código 06. Esto resolvería definitivamente nuestro problema con respecto a la respuesta dada en el terminal.
Voy a explicar por qué eso no resolvería el problema de hecho. En algunos casos, incluso crearía otros problemas. Y el motivo de ello es bastante simple, querido y estimado lector. El problema es que tú estarías intentando resolver el resultado de la línea nueve del código, cuando, en realidad, estarías olvidándote de que existe la línea ocho, donde utilizamos datos del tipo entero. Entonces, en lugar de corregir el problema, podrías estar haciendo la cosa aún más confusa, generando así un nuevo problema en algún otro punto del código.
Esta es definitivamente una situación típica que, de hecho, termina volviéndose bastante embarazosa. Por esta razón, es raro ver un código práctico que haga uso de una plantilla con múltiples tipos, al menos aquí en MQL5. Ya en lo que respecta a C, principalmente C++, este tipo de cosa es muy común y ocurre todo el tiempo.
Entonces, lo que estoy presentando aquí sirve más como una curiosidad que como algo que tú vayas a utilizar en el día a día. Sin embargo, cuando sea necesario aplicarlo, tú recordarás rápidamente los posibles problemas que eso puede generar. Así que, querido lector, no te sientas mal por pensar en alguna solución para resolver este tipo de conflicto. Pues, hasta donde sé, no existe una forma simple de resolverlo. Incluso en C++, donde esto ocurre, muchas veces es necesario hacer verdaderos malabarismos para sortear el problema. Y créeme, no es nada divertido.
Consideraciones finales
Este artículo mostró cómo lidiar con una de las situaciones más molestas y complicadas en términos de programación con las que tú podrías encontrarte: el uso de tipos diferentes en una misma plantilla de función o procedimiento. Aunque nos hemos enfocado casi todo el tiempo solo en funciones, todo lo que se ha visto aquí sirve y puede aplicarse a procedimientos, tanto en el uso de paso por valor como en los casos en que utilizamos paso por referencia. Sin embargo, para no hacer el material cansino y aburrido, no mostraré esos casos en acción.
Así queda definido que tú, mi querido lector, practiques y procures formular pequeños fragmentos de código para estudiar esos casos que mencioné hace poco, como trabajar con paso por referencia y el uso de plantillas de procedimiento, ya que, en este primer momento, no trabajaremos necesariamente con eso.
En el anexo, tendrás a tu disposición buena parte de los códigos presentados aquí. Los códigos que no estarán disponibles son modificaciones simples de los códigos presentados en el anexo. De cualquier manera, practicar lo que se mostró aquí ayudará a fijar mejor el contenido. En el próximo artículo, hablaremos más sobre plantillas. Entonces, hasta pronto.
Traducción del portugués realizada por MetaQuotes Ltd.
Artículo original: https://www.mql5.com/pt/articles/15668
Advertencia: todos los derechos de estos materiales pertenecen a MetaQuotes Ltd. Queda totalmente prohibido el copiado total o parcial.
Este artículo ha sido escrito por un usuario del sitio web y refleja su punto de vista personal. MetaQuotes Ltd. no se responsabiliza de la exactitud de la información ofrecida, ni de las posibles consecuencias del uso de las soluciones, estrategias o recomendaciones descritas.





- Aplicaciones de trading gratuitas
- 8 000+ señales para copiar
- Noticias económicas para analizar los mercados financieros
Usted acepta la política del sitio web y las condiciones de uso