Español
preview
Do básico ao intermediário: Template e Typename (II)

Do básico ao intermediário: Template e Typename (II)

MetaTrader 5Exemplos |
130 0
CODE X
CODE X

Introdução


No artigo anterior Do básico ao intermediário: Template e Typename (I), começamos a falar de um tema bastante complexo, porém muito divertido e bastante interessante. Que é justamente a criação de templates de funções e procedimentos. Visto que este, é de fato um tema bastante complicado de ser abordado e explicado em poucos artigos. Iremos dividir ele em diversos artigo. Mas não irei me aprofundar em demasia apenas neste tema. O explorando em profundidade, antes de passar a falar de outros temas, igualmente interessantes. Visto que existem algumas coisas, que somente farão sentido, se outros temas também forem abordados.

No entanto, iremos trabalhar por um tempo, apenas neste tema. Isto até que tenhamos construído uma base suficientemente sólida e larga, para podermos entrar em outros temas, antes de voltarmos a falar de templates. Pois de fato, este é um tema, muito amplo e vasto. Como você irá ver no decorrer dos próximos artigos que serão postados. Mas de qualquer maneira, é necessário explicar alguns pontos dos quais não mencionei no artigo anterior, justamente para não tornar aquele artigo demasiadamente complicado e assustador. Quero que você, meu caro leitor, se sinta à vontade e confortável estudando cada um destes artigos. E que eles venham a lhe servir como trampolim, a fim de que você comece na programação de uma forma mais correta e segura. Tendo pelo menos uma boa noção de cada ferramenta disponível no MQL5.

Apesar de que, grande parte do que estamos abordando aqui, serve também para outras linguagens. Desde que você faça as devidas considerações a fim de aplicar os conceitos da maneira adequada.

Ok, tido tais palavras, para que possamos nos posicionar a respeito do que iremos tratar aqui. Chegou a hora de dar uma relaxada. Remover possíveis distrações ao seu redor, e focar no que será explicado neste artigo. Pois aqui iremos nos tratar um pouco mais sobre templates.

O conteúdo exposto aqui, visa e tem como objetivo, pura e simplesmente a didática. De modo algum deve ser encarado como uma aplicação final, onde o objetivo não seja o estudo dos conceitos aqui mostrados.


Templates e mais templates

Uma das coisas mais interessantes a respeito de templates, é o fato de que se forem bem planejados. Eles, acabam se tornando uma ferramenta indispensável. Isto por que, você acaba criando o que muitos poderiam chamar de modelo de implementação rápida. Ou seja, você deixa de precisar ficar programando diversas coisas, e passa a programar apenas parte dos detalhes necessários.

Muito provavelmente, você, meu caro e estimado leitor, não tem a mínima noção do que estou falando neste momento. Mas conforme você for estudando, e pegando prática como programador. Acabará notando que muitas coisas, podem ser feitas, de maneira muito mais rápida, se forem adotadas certas medidas. E saber e entender cada ferramenta que está a nossa disposição, faz toda a diferença na hora de escolher qual o melhor caminho a ser tomado. Novamente, NÃO SE APEGUE A NOMES. Procure entender o conceito adotado e com o tempo, você conseguirá assumir as rédeas e construir seu próprio caminho.

Então vamos começar com uma implementação bem simples. Baseada no que foi visto no artigo anterior. Esta pode ser vista logo abaixo.

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

Neste código 01, que foi explicado no artigo anterior. Temos a capacidade de utilizar a mesma função, a fim de trabalhar com tipos diferentes de dados. Porém, existe algo aqui, que acaba tornando as coisas um tanto quanto chatas. Isto para não dizer outra coisa. O problema é o seguinte: Observe que nas linhas seis e sete temos dados de tipos diferentes. Como você deve ter notado, se estou o conteúdo do artigo anterior. O compilador consegue lidar com isto perfeitamente bem, criando as funções sobrecarregadas de modo que no final.

Não precisamos nos preocupar em definir que tipo de dados estamos utilizando. Mas, e é aqui onde mora a parte chata. Os tipos de dados utilizados aqui, precisam ser iguais. Ou seja, se o primeiro argumento, passado para o template da função Sum, for do tipo float, o segundo argumento, OBRIGATORIAMENTE, também precisa ser do tipo float. Caso contrário um erro, ou no melhor dos casos, um alerta será disparado pelo compilador. Dizendo que existe um problema entre os tipos de dados utilizados.

Para entender isto melhor, vamos modificar uma das linhas. Pode ser a linha seis ou sete deste código 01. De forma que teremos tipos diferentes sendo repassados para a função. Lembre-se, a função de fato ainda não existe. O compilador precisa construir ela com base no template que estamos fornecendo. Assim sendo, o código 01, foi modificado como mostrado logo abaixo.

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

Note que mudei apenas e somente os valores que estavam sendo repassados para a função. No caso, decidi mexer na linha sete, como você pode observar, ao comparar o código 01 com o código 02. Porém apesar de ter feito, apenas e somente está pequena e inocente mudança no código. Veja o que resultou quando tentamos compilar o mesmo.

Imagem 01

Estas mensagens vista nesta imagem 01, podem variar ligeiramente, dependendo de cada caso específico. Porém, você pode acabar ficando completamente perdido, ao notar que o compilador não conseguiu estabelecer uma base de ligação. Sendo que a única mudança feita foi nos valores, presentes na linha sete do código. Porém, é justamente esta, é justamente a parte chata, da qual eu estava me referindo. Como o nosso exemplo aqui, é puramente didático. Você pode pensar: Bem, mas isto não faz sentido. Por que o compilador não está conseguindo criar o código com base no template que estamos fornecendo? O motivo é que, pelo fato do primeiro argumento ser um valor inteiro, NÃO PDOEMOS colocar no segundo argumento um valor que seja em ponto flutuante. Ou vice versa, onde primeiro colocamos um valor do tipo ponto flutuante, para depois colocar um valor interior. 

Da maneira como o template da função Sum está sendo definido no código 02. O compilador, não consegue estabelecer uma função que seja adequada para efetuar o que se espera da função template. No caso a função Sum. Nestas horas, muitos iniciantes acabam desistindo e partindo para outra forma de abordar o problema. Sendo que tudo poderia ser resolvido de maneira muito simples. Porém para resolver este tipo de questão da maneira adequada, precisamos primeiro entender o que se espera da função, ou procedimento, que está sendo utilizado como template. A fim de criar outros procedimentos ou funções sobrecarregadas.

Ok, como aqui o objetivo é pura e simplesmente a didática. A função que estamos usando é bem simples e direta. Tudo que esperamos da mesma, é que ela some dois valores. E nos retorne o resultado desta soma. Então se você, já tiver tentando somar um valor do tipo ponto flutuante, com um valor inteiro, sabe perfeitamente que o resultado é um valor do tipo ponto flutuante.

Este tipo de coisa é conhecido como conversão de tipo, ou typecasting. E já falamos sobre isto, quando foi explicado sobre variáveis e constantes. Basicamente tudo se resume ao que é visto na imagem logo abaixo.

Imagem 02

Pois bem, com base no que é visto nesta imagem 02. Sabemos que o tipo double é o alvo em todos os casos em que fazemos operações matemáticas, como tipos diferentes. Como está sendo feito na linha sete do código 02. Sabendo disto, e tendo a noção de que o compilador não está usando uma função sobrecarregada, mas sim um template, podemos forçar o compilador a entender que estamos cientes do que está ocorrendo. E podemos mudar o código 02, para algo que realmente funcione. Com pode ser visto na implementação logo na sequência.

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

Observe que agora, neste código 03, estamos utilizando o mesmo código visto no código 02. Porém, aqui estamos forçando o compilador a entender, que de fato, estamos cientes que estamos lidando com um tipo double. Mesmo que o valor, seja uma variável do tipo inteiro. Esta conversão explícita de tipo, que estamos fazendo, ao adicionar o termo que é visto na linha sete do código 03. Que diferencia esta mesma linha sete do código 02. Torna possível que o compilador utilize a função template Sum de forma a criar uma sobrecarga adequada. Com isto o resultado final pode ser observado logo abaixo.

Imagem 03

O detalhe é que esta solução que estou apresentando, não é a única solução possível. Podemos fazer algo ligeiramente diferente e obter o mesmo tipo de resultado. Tudo é uma questão de entender o que queremos implementar de fato. E da nossa necessidade naquele momento. Portanto, mais que entender ou simplesmente decorar um código, ou pior, usar a velha tática de cópia a cola ( CTRL+C e CTRL+V). Precisamos de fato, compreender os conceitos que estão sendo utilizados. Somente assim conseguiremos, ser capazes de resolver qualquer problema, relacionado a programação, quando eles vierem a serem colocados em nossa frente.

Ok, uma outra maneira de resolver este mesmo problema é travar um dos argumentos em um tipo especifico. Este apesar de muitos não acharem ser algo adequado, pode sim resolver diversos tipos de problemas, em situações muito específicas. Onde sabemos de antemão que sempre estaremos utilizando um dado tipo de informação, em um argumento específico.

Assim, supondo que o primeiro argumento, sempre e SEMPRE será do tipo inteiro. Podemos fazer algo como mostrado logo abaixo.

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

O resultado deste código 04 é o mesmo do código 03. Ou seja, a imagem 03. Porém, toda via e, entretanto. Aqui no código 04, estamos travando, ou dizendo ao compilador, que um dos argumentos da função template, deverá ser sempre do tipo inteiro. No caso estamos usando um tipo de 16 bits com sinal. Mas poderia ser qualquer outro. Note que, nesta situação específica, já não precisamos fazer uma conversão de tipo, de maneira explicita. Isto por que, o compilador já saberá como lidar com esta situação. Porém, é importante frisar de que, o segundo argumento será definido pelo compilador. Isto durante a fase de criação do executável. Por conta disto, teremos uma função para responder a chamada da linha seis, onde utilizamos apenas tipos inteiros. E outra função, somente para responder a linha sete, onde estamos utilizando um tipo inteiro e um outro que é do tipo ponto flutuante.

Agora pergunto: Este tipo de coisa é ou não é, algo muito divertido de brincar e praticar? Note que estamos dizendo ao compilador como ele deverá trabalhar. E ao fazer isto, conseguimos sem muito esforço criar diversas possibilidades e modos de criar uma mesma coisa.

Mas não pense que terminou. Temos ainda um outro modo de resolver este mesmo tipo de problema. Só que neste caso, a coisa acaba se tornando um pouco mais complicada depois. Nos trazendo um outro tipo de problema que, em um outro momento irei explicar como lidar com ele. Porém, mesmo assim, é algo que de fato merece ser mostrado, já que se encaixa em diversas situações, podendo até mesmo abrir as portas para todo um mundo novo de possibilidade de uso e formas de trabalhar com o MQL5.

No entanto, para evitar que você acabe por confundir as coisas, vamos ver isto em um outro tópico. Assim, você pode primeiro estudar o que foi visto até aqui. Praticar e assimilar estes conceitos. Para somente depois vir a se aventurar no que será visto logo na sequência.


Um template, múltiplos tipos

No tópico anterior, vimos como lidar com uma coisa bastante chata, que as vezes acaba nos limitando em termos de utilização de um template. Porém, aquilo que foi visto ali, é apenas a primeira parte de uma enormidade de coisas que podemos realmente fazer. O que irei mostrar aqui neste tópico, não é algo muito comum. Pelo menos não aqui no MQL5. Já que até o momento, não me lembro de ter visto, ninguém de fato utilizando tal metodologia. Então, muitos podem achar que tal possibilidade não existe, ou não tem como ser implementada. Ficando assim de mãos atadas, sem poder conseguir atingir seu objetivo primário.

Porém, o fato de você não ter visto, ou ouvido relatos de que pode ou não utilizar algo. Não significa que aquilo, não existe, ou não é aceito pela linguagem de programação. Muitas das vezes, o problema nem é este. Mas sim, outros tipos de problemas que podem vir a acontecer, justamente por conta de estarmos fazendo um uso errado. Ou, como é na grande parte dos casos. Não ter de fato compreendido alguns conceitos referentes a alguma ferramenta de programação.

Muito bem, no artigo anterior, expliquei, que este T que vemos sendo utilizado em um template. É na verdade um identificador, que será utilizado pelo compilador a fim de que, ele, o compilador, identifique localmente, um determinado tipo. Isto a fim de saber como lidar com a informação que estará chegando. Pois bem, se você entende o conceito de identificador, sabe perfeitamente que se um identificador for corretamente declarado, ele irá na verdade se comportar, como uma variável ou como uma constante.

No caso do identificador, que é o tal T, que estamos vendo na linha dez do código 04. Ele NÃO É UMA VARIÁVEL. Já que uma vez definido, não poderá ser alterado. Então, claramente ele é uma constante. Porém, é uma constante que é definida pelo compilador, e que representa o tipo de dado, que se espera.

Preste atenção, meu caro leitor, quando me refiro ao tipo de dado, estou me referindo ao conteúdo visto na imagem 02. Por isto é importante, que você consiga compreender o conceito, e não decorar formulas ou modelos de implementação. Tendo de fato compreendido, este conceito, que apesar de ser simples, logo você irá notar que é bem poderoso. Podemos criar uma solução, quase que mágica. Que conseguirá entender, em quase todo tipo de cenário, como um template deverá ser utilizado para se criar algo. Seja uma função, seja um procedimento. Para isto, precisaremos adicionar, tantos identificadores, quanto for necessário, para cobrir a maior quantidade possível de casos permitidos. E quem irá decidir, isto é, você, meu caro leitor. Isto durante a fase de implementação do código.

Então podemos modificar o código 04 e buscar algo mais parecido código 02. Porém sem o problema encontrado no código 02. Onde haveria apenas e somente um único tipo de dado, podendo ser utilizado. Parece complicado, mas na prática é bem mais simples do que você possa imaginar. Veja, como seria então a solução do problema, aplicando este novo conceito, de utilizar múltiplos 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

Aqui é onde, como dizem, a porca começa a torcer o rabo. Isto por que, as coisas começam de fato a ficarem bem mais complicadas e confusas. Apesar de eu estar pegando leve, e mostrando as coisas aos poucos. Porém o problema aqui, é justamente isto que estamos fazendo na linha doze, onde é feita a declaração do template da função Sum.

Quando executado, este código 05, irá produzir o resultado que é visto na imagem logo abaixo.

Imagem 04

Muito bem, estou destacando um ponto nesta imagem, justamente para chamar a sua atenção, meu caro leitor. Note que o resultado da operação está errado. Ou melhor, dizendo, ele NÃO CORRESPONDE ao valor esperado. Já que o valor que seria esperado aqui, seria o mesmo que podemos visualizar na imagem 03. Mas por que? Bem, você pode imaginar que é a forma como a linha doze está escrita. Já que aquilo dali aparentemente não faz o menor sentido. Porém, o problema NÃO ESTÁ NA LINHA DOZE, como era de se supor, mas sim na linha nove, ou ainda na linha dezesseis. Dependendo da forma como você venha a olhar o código. Ou da maneira que o código foi pensado para ser implementado. Por isto é importante entender os conceitos adotados e sempre pensar antes de sair escrevendo código a esmo. Parecendo um maluco desenfreado.

Talvez você não esteja acreditando em minhas palavras. Então NÃO VAMOS MEXER NA LINHA DEZESEIS, e sim na linha nove, trocando a ordem em que os valores estão sendo declarados. Isto nos leva ao código visto logo abaixo.

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

Observe que somente mudamos aquilo que foi dito. E ao executar o código, o resultado é o que podemos ver logo abaixo.

Imagem 05

Minha nossa senhora santíssima. Por DEUS, que coisa mais maluca e sem noção. Isto não é coisa de DEUS. Isto é coisa do CÃO. Agora fiquei realmente com medo de mexer com programação antes de entender melhor algumas coisas. Eu estava achando tudo muito simples e fácil. Até já estava me considerando um programador, já que conseguia escrever pequenos trechos de código e estes estavam funcionando. Mas vendo isto. Percebo que ainda não sei absolutamente nada. Que estou apenas começando a aprender o que de fato é ser um programador.

Calma meu caro leitor, não é para tanto. De fato, as coisas nem sempre são tão simples, como pode parecer à primeira vista. Ainda mais quando entramos em uma zona de conforto e não saímos de lá. Mas o problema é que muita gente se considera um programador de qualidade. E por isto, simplesmente para de estudar. Ficando assim paralisado. Pois digo a você, e posso afirmar isto. 

Um bom programador, NUNCA PARA DE ESTUDAR. Ele está sempre se atualizando e estudando novos conceitos. SEMPRE.

Este tipo de coisa que você acabou de ver acontecer aqui, é de fato, algo que muitas das vezes DESTROI a moral de um programador. Ainda mais quando cai em seu colo, um código que a priori está correto. E de fato, está correto. Porém o mesmo, sempre reporta resultados errados, e sem um motivo aparente. Por isto, não se iluda com a ideia de que, por saber escrever alguns códigos, já pode ser considerado um programador. Para chegar neste nível, você tem que passar por muita coisa. E muitas delas, apenas o tempo poderá lhe ensinar. Eu já passei por este tipo de situação que estou mostrando, e digo a você: Penei muito tempo para aprender a resolver este tipo de situação. Isto a ponto de conseguir entender por que hora um código funcionava, hora ele ficava completamente maluco. Dado resultados aparentemente sem nenhum sentido.

Mas vamos entender o que está acontecendo aqui, tanto no código 05 quanto no código 06. Pois eles são a mesma coisa. Sendo a única diferença, esta simples mudança na sequência de declaração dos parâmetros da linha nove.

Quando o compilador encontra uma chamada ao template que estamos declarando na linha doze. Ele irá verificar que tipo de dado, cada argumento está utilizando. Da mesma forma que era feito antes. Criando assim uma função sobrecarregada. Isto para atender aquela chamada específica. Caso nenhuma função criada antes consiga atender, o atual modelo que precisa ser utilizado.

Como na linha doze, temos dois typename a fim de capturar, dois tipos diferentes. Podemos utilizar dois tipos de dados com tipos completamente diferentes. Coisa que antes não era possível de ser feita. Isto cobre perfeitamente bem, uma grande quantidade de casos. Já que basicamente os valores podem ser inteiros ou do tipo ponto flutuante, sem nos preocuparmos com isto a princípio. Porém, por questões específicas, esta modelagem não cobre todos os casos. Mas isto não vem ao caso. Porém sabendo que podemos tanto utilizar dados do tipo inteiro ou do tipo ponto flutuante, ao mesmo tempo e sem problemas. Podemos ter um template quase perfeito.

Assim o tipo de dado que estiver sido declarado no primeiro argumento, será colocado na constante T1 pelo compilador. Já o tipo de dado usado no segundo argumento, será colocado na constante T2 do compilador. Então quando na linha dezesseis, fizermos a conversão de tipo, afim de corresponder ao tipo de retorno da função Sum. Poderemos ter um resultado como mostrado nas imagens 04 ou 05.

Como apenas olhando o código do template, temos uma vaga visão do que o compilador estará de fato construindo. Fica um tanto quanto difícil entender, por que de termos resultados tão diferentes. Apenas por conta da simples mudança na ordem de declaração dos valores na linha nove.

Para facilitar este entendimento, vamos ver qual seria a função escrita pelo compilador em ambos os casos. Então, supondo que não estivéssemos utilizando template e sim uma codificação tradicional. Teríamos como sendo o código 05, o que pode ser visto logo abaixo.

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, quando executado irá produzir exatamente o que é visto na imagem 04. Já o código 06, se fosse produzido em um modelo de programação tradicional. Teria como conteúdo interno, o que pode ser visto logo na sequência.

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

Da mesma forma que o código 07, daria origem a imagem 04, este código 08, daria origem a imagem 05. Agora note que a diferença entre um e outro código é muito sutil. Podendo passar batido por diversas situações sem que, ao menos venhamos a perceber que existe algo de estranho ali. No entanto, diferente do código que faz uso de templates. Nestes códigos 07 e 08, você logo acabaria achando a causa do problema. Já que ao observar os resultados. Você astutamente iria atras da função Sum que estaria apresentando o problema, e acabaria por notar, que existe uma conversão explicita de tipo. E que esta seria a responsável por estar forçando o código a nos apresentar uma resposta errada. Corrigindo então o problema.

Porém a mesma coisa, não seria facilmente notada quando o template estivesse sendo utilizado. Neste caso, você iria penar, sofre, e até começar a desistir de continuar naquele caminho. E se insistisse bastante, em algum momento, você acabaria conseguindo notar onde estaria o erro. No entanto. antes de que você pense: Bem, tudo que precisamos fazer, seria utilizar uma conversão de tipo explicita para o tipo double, na linha dezesseis do código 05 ou do código 06. Isto resolveria definitivamente o nosso problema com relação a resposta que estria sendo dada no terminal.

Eu vou lhe dizer por que isto não resolveria de fato o problema. E em alguns casos até criaria outros problemas. E o motivo para isto bastante simples, meu caro e estimado leitor. O problema é que você estaria tentando resolver o resultado da linha nove do código. Quando na verdade, estaria se esquecendo, de que existe a linha oito. Onde estamos utilizando dados que fazem uso, única e exclusivamente do tipo inteiro. Então, ao invés de corrigir o problema você poderia estar tornando a coisa ainda mais confusa. Gerando assim um outro problema em algum outro ponto do código.

Esta é definitivamente uma típica situação, que é de fato, acaba se tornando bastante embaraçosa. Por conta disto, raramente você irá ver um código prático, fazendo uso de um template com múltiplos tipos. Pelo menos aqui no MQL5. Já no que diz respeito ao C, mas principalmente ao C++. Este tipo de coisa é muito viciante e acontece a todo momento.

Então o que estou apresentando aqui, serve muito mais como uma curiosidade do que algo que você irá de fato utilizar no dia a dia. Porém quando vier a ser necessário ser aplicado. Você logo irá se lembrar dos possíveis problemas, que isto pode gerar. Então, não se sinta mal em pensar alguma solução, a fim de resolver este tipo de conflito, meu caro leitor. Pois até onde sei, não existe uma forma simples de resolver isto. Até em C++ onde isto acontece. Muitas vezes temos que fazer verdadeiros malabarismos para conseguir contornar o problema. E acredite não é nada engraçado.


Considerações finais

Este artigo foi mostrado como lidar com uma das situações mais chatas e complicadas em termos de programação, que você poderá vir a enfrentar. O uso de tipos diferentes em um mesmo template de função ou procedimento. Apesar de aqui termos focado quase o tempo todo apenas em funções. Tudo que foi visto aqui, serve e pode ser aplicado a procedimentos. Tanto no uso de passagem por valor, quanto nos casos em que venhamos a utilizar passagem por referência. Porém, para não tornar o material chato e cansativo. Já que este conteúdo pode se tornar muito maçante. Não irei de fato mostrar estes casos sendo utilizados.

Fica assim definido, que você meu caro leitor, pratique e procure formular pequenos trechos de código, a fim de estudar estes casos que mencionei a pouco. Como trabalhar com passagem por referência, e o uso de templates de procedimentos. Já que neste primeiro momento não iremos mexer necessariamente com isto.

No anexo, você terá disponível boa parte dos códigos vistos aqui. Sendo que, os que não estarão disponíveis. Será pelo motivo de serem modificações simples dos presentes no anexo. De qualquer maneira, ao praticar o que foi mostrado aqui, é que lhe ajudará a fixar melhor este conteúdo. No próximo artigo, iremos continuar falando mais sobre templates. Então até breve.

Arquivos anexados |
Anexo.zip (1.84 KB)
Desenvolvimento de um EA baseado na estratégia de rompimento do intervalo de consolidação em MQL5 Desenvolvimento de um EA baseado na estratégia de rompimento do intervalo de consolidação em MQL5
O artigo descreve os passos para criar um EA (Expert Advisor) que aproveita os rompimentos de preços após períodos de consolidação. Ao identificar esses intervalos e estabelecer os níveis de rompimento, os traders podem automatizar suas decisões de negociação com base nessa estratégia. O EA foi projetado para fornecer pontos claros de entrada e saída, evitando rompimentos falsos.
Redes neurais em trading: Rede neural espaço-temporal (STNN) Redes neurais em trading: Rede neural espaço-temporal (STNN)
Neste artigo, discutiremos o uso de transformações espaço-temporais para prever com eficácia o movimento futuro dos preços. Para melhorar a precisão das previsões numéricas na STNN, foi proposto um mecanismo de atenção contínua que permite ao modelo considerar melhor os aspectos relevantes dos dados.
Algoritmo de comportamento social adaptativo — Adaptive Social Behavior Optimization (ASBO): Método de Schwefel, Box-Muller Algoritmo de comportamento social adaptativo — Adaptive Social Behavior Optimization (ASBO): Método de Schwefel, Box-Muller
Este artigo apresenta uma imersão fascinante no mundo do comportamento social de organismos vivos e sua influência na criação de um novo modelo matemático — ASBO (Adaptive Social Behavior Optimization). Exploramos como os princípios de liderança, vizinhança e cooperação, observados em sociedades de seres vivos, inspiram o desenvolvimento de algoritmos de otimização inovadores.
Simulação de mercado (Parte 03): Uma questão de performance Simulação de mercado (Parte 03): Uma questão de performance
Muitas vezes somos obrigados a dar um passo para trás para logo depois dar alguns passos a frente. Neste artigo irei mostrar todas as mudanças que foram necessárias serem feitas para que os indicadores de Mouse e Chart Trade não viessem a ter a sua performance comprometidas. Como bônus irei já apresentar outras mudanças que ocorreram em outros arquivos de cabeçalho, que serão muito usados no futuro.