
Von der Grundstufe bis zur Mittelstufe: Templates und Typename (II)
Einführung
Im vorigen Artikel „Von der Grundstufe zur Mittelstufe: Templates und Typename (I)“ haben wir uns mit einem ziemlich komplizierten, aber sehr faszinierenden Thema beschäftigt: der Erstellung von Templates für Funktionen und Prozeduren. Da es recht schwierig ist, dieses Thema in mehreren Artikeln zu behandeln und zu erklären, werden wir es auf mehrere Artikel aufteilen. Wir werden jedoch nicht zu tief in dieses Thema eindringen, bevor wir zu anderen, nicht weniger interessanten Themen übergehen, denn es gibt Dinge, die nur Sinn machen, wenn auch andere Themen angesprochen werden.
Aber wir werden nur eine Zeit lang an diesem Thema arbeiten, bis wir eine ausreichend solide und breite Basis geschaffen haben, sodass wir zu anderen Themen übergehen können, bevor wir zu den Templates zurückkehren. Schließlich ist dieses Thema sehr umfangreich. Auf jeden Fall ist es notwendig, einige Punkte zu klären, die wir im vorherigen Artikel nicht erwähnt haben, um das Thema nicht zu verkomplizieren. Ich möchte, dass Sie sich bei der Lektüre dieser Artikel wohlfühlen und dass sie Ihnen helfen, korrekter und sicherer zu programmieren, indem Sie zumindest ein gutes Verständnis für jedes in MQL5 verfügbare Werkzeug haben.
Obwohl vieles von dem, was hier beschrieben wird, auch auf andere Sprachen anwendbar ist, muss der Anwendung dieser Konzepte gebührende Aufmerksamkeit gewidmet werden.
Nach diesen Worten ist es an der Zeit, sich zu entspannen, mögliche Ablenkungen zu beseitigen und sich auf das zu konzentrieren, was in diesem Artikel besprochen wird. Hier werden wir ein wenig mehr über Templates sprechen.
Die hier zur Verfügung gestellten Materialien sind ausschließlich für didaktische Zwecke bestimmt. In keinem Fall sollte dieser Antrag als endgültig betrachtet werden, der anderen Zwecken als dem Studium der vorgestellten Konzepte dienen soll.
Templates, mehr Templates
Eine der interessantesten Eigenschaften von Templates ist, dass sie bei richtiger Planung zu einem unverzichtbaren Werkzeug werden. Dies geschieht, weil wir das schaffen, was viele als ein schnelles Implementierungsmodell bezeichnen würden. Mit anderen Worten: Wir müssen nicht mehr alles programmieren, sondern nur noch einen Teil der notwendigen Elemente.
Höchstwahrscheinlich haben Sie keine Ahnung, worum es sich handelt. Aber wenn Sie das Programmieren lernen und üben, werden Sie feststellen, dass viele Dinge viel schneller erledigt werden können, wenn Sie bestimmte Schritte unternehmen. Wenn wir alle Instrumente kennen und verstehen, die uns zur Verfügung stehen, können wir den besten Weg wählen, BINDEN SIE SICH NICHT AN NAMEN. Versuchen Sie, das akzeptierte Konzept zu verstehen, und mit der Zeit werden Sie in der Lage sein, das Brett selbst in die Hand zu nehmen und Ihren eigenen Weg zu gehen.
Beginnen wir nun mit einer sehr einfachen Implementierung, die auf dem im vorherigen Artikel Gezeigten basiert. Es ist unten dargestellt:
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. //+------------------------------------------------------------------+
Code 01
Im Code 01, der im vorherigen Artikel besprochen wurde, haben wir die Möglichkeit, dieselbe Funktion für die Arbeit mit verschiedenen Datentypen zu verwenden. Allerdings gibt es hier etwas Ärgerliches, um es gelinde auszudrücken. Das Problem besteht darin, dass wir in den Zeilen 06 und 07 Daten unterschiedlicher Art haben. Wie Sie nach der Lektüre des vorherigen Artikels vielleicht bemerkt haben, löst der Compiler dieses Problem perfekt, indem er überladene Funktionen erstellt. So müssen Sie am Ende nicht feststellen, welcher Datentyp verwendet wird. Aber genau hier liegt der unangenehme Teil: Die hier verwendeten Datentypen müssen gleich sein, d.h. wenn das erste Argument, das an das Temaplate für die Funktion zu summieren übergeben wird, vom Typ float ist, dann MUSS das zweite Argument ebenfalls vom Typ Float sein. Andernfalls gibt der Compiler einen Fehler oder bestenfalls eine Warnung aus, dass es ein Problem zwischen den verwendeten Datentypen gibt.
Zum besseren Verständnis sollten wir eine der Zeilen ändern. Dies kann Zeile 06 oder 07 von Code 01 sein, sodass unterschiedliche Typen an die Funktion übergeben werden. Denken Sie daran, dass die Funktion noch nicht wirklich existiert. Der Compiler muss sie auf der Grundlage des bereitgestellten Templates erstellen. Daher haben wir den Code 01 wie unten dargestellt geändert.
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. //+------------------------------------------------------------------+
Code 02
Bitte beachten Sie, dass nur die Werte, die an die Funktion übergeben wurden, geändert wurden. Wie Sie sehen, haben wir in diesem Fall beschlossen, Zeile 07 zu ändern, indem wir Code 01 mit Code 02 verglichen haben. Doch trotz dieser kleinen und unschuldigen Änderung im Code, sehen Sie sich an, was passiert, wenn wir versuchen, ihn zu kompilieren.
Abbildung 01
Die in Abbildung 01 dargestellten Meldungsdaten können im Einzelfall leicht abweichen. Der Leser könnte verwirrt sein, wenn er feststellt, dass der Compiler keine Referenzbasis erstellt hat, da die einzige Änderung die Werte in Zeile 07 des Codes sind. Das ist genau der Reiz, über den wir vorhin gesprochen haben. Da unser Beispiel rein didaktisch ist, könnte man denken: „Warum kann der Compiler keinen Code auf der Grundlage des bereitgestellten Templates generieren?“ Der Grund dafür ist, dass das erste Argument eine ganze Zahl ist und wir im zweiten Argument keine Fließkommazahl unterbringen können. Oder umgekehrt, wenn wir zuerst einen Gleitkommawert und dann einen Ganzzahlwert eingeben.
Da das Summenfunktions-Template in Code 02 definiert ist, kann der Compiler keine geeignete Funktion erzeugen, die das tut, was von der Template-Funktion, d. h. von der Summenfunktion, erwartet wird. Viele Anfänger geben schließlich auf und wählen einen anderen Lösungsansatz, obwohl alles ganz einfach gelöst werden könnte. Um dieses Problem korrekt zu lösen, muss man jedoch zunächst verstehen, was von der als Template verwendeten Funktion oder Prozedur erwartet wird, um andere überladene Prozeduren oder Funktionen zu erstellen.
Da unser Ziel rein didaktischer Natur ist, ist die von uns verwendete Funktion sehr einfach. Alles, was wir von ihr erwarten, ist, dass sie zwei Werte addiert und das Ergebnis dieser Summe zurückgibt. Wenn Sie bereits versucht haben, einen Fließkommawert zu einer Ganzzahl zu addieren, dann denken Sie daran, dass das Ergebnis ein Fließkommawert sein wird.
Dieser Vorgang ist als Typkonvertierung oder Typecasting bekannt, und wir haben darüber gesprochen, als wir Variablen und Konstanten besprochen haben. Im Grunde genommen ergibt sich folgendes Bild:
Abbildung 02
Anhand von Abbildung 02 wissen wir, dass der Typ double immer dann verwendet wird, wenn mathematische Operationen mit verschiedenen Typen durchgeführt werden, wie es in Zeile 07 von Code 02 der Fall ist. Mit diesem Wissen und dem Wissen, dass der Compiler ein Template und keine überladene Funktion verwendet, können wir dem Compiler zu verstehen geben, dass wir uns dessen bewusst sind, was geschieht. Wir können also den Code 02 so ändern, dass er tatsächlich funktioniert, wie in der nachstehenden Implementierung gezeigt.
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. //+------------------------------------------------------------------+
Code 03
Bitte beachten Sie, dass wir bei Code 03 denselben Code wie bei Code 02 verwenden. Hier zwingen wir jedoch den Compiler zu verstehen, dass wir uns bewusst sind, dass wir es mit dem Typ double zu tun haben, auch wenn der Wert eine Variable vom Typ integer ist. Diese explizite Typumwandlung, die durch Hinzufügen eines Terms in Zeile 07 von Code 03 durchgeführt wird, unterscheidet dieselbe Zeile 07 von Code 02 und ermöglicht es dem Compiler, die Template-Funktion für die Summenbildung so zu verwenden, dass die entsprechende Überladung erzeugt wird. Das Endergebnis wird daher wie folgt aussehen:
Abbildung 03
Tatsache ist, dass die von uns vorgestellte Lösung nicht die einzig mögliche ist. Wir können etwas ein wenig anders machen und das gleiche Ergebnis erzielen. Das Wichtigste ist, dass wir genau verstehen, was wir umsetzen wollen und welches Ziel wir im Moment verfolgen. Anstatt also nur den Code auswendig zu lernen oder, schlimmer noch, die alte STRG+C- und STRG+V-Taktik anzuwenden, wollen wir die verwendeten Konzepte wirklich verstehen. Nur so können wir alle Probleme im Zusammenhang mit der Programmierung lösen, sobald sie auftreten.
Eine andere Lösung besteht darin, eines der Argumente auf einen bestimmten Typ zu beschränken. Obwohl viele Menschen diese Methode nicht für geeignet halten, ermöglicht sie es uns, verschiedene Arten von Problemen in ganz bestimmten Situationen zu lösen, wenn wir im Voraus wissen, dass wir immer eine bestimmte Art von Informationen in einem bestimmten Argument verwenden werden.
Wenn wir also davon ausgehen, dass das erste Argument IMMER vom Typ Integer ist, können wir so vorgehen:
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. //+------------------------------------------------------------------+
Code 04
Das Ergebnis dieses Codes ist dasselbe wie das des Codes 03, d. h. das Bild 03. In Code 04 teilen wir dem Compiler jedoch mit, dass eines der Argumente für die Template-Funktion immer vom Typ Ganzzahl sein muss. In diesem Fall wird ein vorzeichenbehafteter 16-Bit-Typ verwendet, es kann aber auch ein anderer Typ sein. Beachten Sie, dass wir in dieser Situation keine explizite Typkonvertierung durchführen wollen. Das liegt daran, dass der Compiler bereits weiß, was in dieser Situation zu tun ist. Es ist jedoch wichtig zu betonen, dass das zweite Argument vom Compiler in der Phase der Erstellung der ausführbaren Datei bestimmt wird. Daher werden wir eine Funktion haben, um den Aufruf in Zeile 06 zu beantworten, wo wir nur Integer-Typen verwenden, und eine andere Funktion, um in Zeile 07 zu antworten, wo wir Integer-Typ und Gleitkomma verwenden.
Nun frage ich Sie: Macht es nicht Spaß, in solchen Szenarien zu spielen und zu üben? Bitte beachten Sie, dass wir dem Compiler sagen, wie er arbeiten soll. Auf diese Weise schaffen wir mühelos verschiedene Möglichkeiten, ein und dieselbe Sache zu entwickeln.
Aber denken Sie nicht, dass es vorbei ist. Es gibt eine andere Möglichkeit, dieses Problem zu lösen. In diesem Fall werden die Dinge jedoch etwas komplizierter, und es ergibt sich eine andere Art von Problem, dessen Lösung ein anderes Mal gezeigt werden soll. Dennoch verdient es wirklich Aufmerksamkeit, da es für verschiedene Situationen geeignet ist und sogar die Türen zu einer neuen Welt von Möglichkeiten für die Verwendung und Arbeit mit MQL5 öffnen kann.
Um jedoch nicht durcheinander zu kommen, werden wir dies in einem anderen Thema behandeln. Machen Sie sich zunächst mit diesen Konzepten vertraut und versuchen Sie erst dann, das zu übernehmen, was als Nächstes passieren wird.
Ein Template, mehrere Typen
Im vorigen Thema haben wir besprochen, wie man mit einem eher unangenehmen Problem umgeht, das uns manchmal bei der Verwendung des Templates einschränkt. Der studierte Stoff ist jedoch nur der erste Teil dessen, was wir tatsächlich tun können. Was wir hier besprechen werden, ist nicht sehr verbreitet, zumindest in MQL5, denn bisher kann ich mich nicht erinnern, dass irgendjemand eine ähnliche Methodik verwendet hat. Viele Menschen denken vielleicht, dass eine solche Möglichkeit nicht existiert oder nicht realisiert werden kann, was bedeutet, dass ihnen die Hände gebunden sind und sie ihr Hauptziel nicht erreichen können.
Die Tatsache, dass Sie etwas noch nicht gesehen oder gehört haben, bedeutet jedoch nicht, dass es nicht existiert oder dass es in einer Programmiersprache nicht akzeptiert wird. In vielen Fällen liegt das Problem nicht einmal darin, sondern in anderen Arten von Problemen, die gerade wegen des Missbrauchs oder (wie in den meisten Fällen) wegen des Missverständnisses einiger Konzepte im Zusammenhang mit einem Programmierwerkzeug auftreten können.
Im vorigen Artikel haben wir erklärt, dass das im Template verwendete T eigentlich ein Bezeichner ist, den der Compiler verwendet, um einen bestimmten Typ lokal zu identifizieren. Dies ist notwendig, um zu wissen, wie man mit den eingehenden Informationen umgeht. Wenn Sie das Konzept des Bezeichners verstehen, dann wissen Sie, dass der Bezeichner, wenn er richtig deklariert ist, wie eine Variable oder eine Konstante wirkt.
Der Bezeichner, also das T in Zeile 10 des Codes 04, ist KEINE VARIABLE, da er nach der Identifizierung nicht mehr geändert werden kann. „Dann ist es offensichtlich eine Konstante.“ Es handelt sich jedoch um eine vom Compiler definierte Konstante, die den erwarteten Datentyp darstellt.
Bitte beachten Sie: Wenn wir über den Datentyp sprechen, beziehen wir uns auf den Inhalt von Bild 02. Daher ist es wichtig, das Konzept zu verstehen, anstatt Formeln oder Implementierungsmodelle auswendig zu lernen. Wenn wir dieses Konzept verstehen, das zwar einfach, aber (wie Sie bald feststellen werden) sehr mächtig ist, können wir eine fast magische Lösung erstellen, die versteht, wie ein Template verwendet werden sollte, um etwas zu erstellen, sei es eine Funktion oder eine Prozedur. Dazu müssen Sie so viele Identifikatoren wie nötig hinzufügen, um so viele zulässige Fälle wie möglich abzudecken. Diese Entscheidung treffen Sie in der Phase der Code-Implementierung.
Dann können wir den Code 04 ändern und etwas finden, das dem Code 02 ähnlicher ist, aber ohne das Problem des Codes 02, in dem nur ein Datentyp verwendet werden konnte. Das hört sich kompliziert an, ist aber viel einfacher, als Sie vielleicht denken. Lassen Sie uns sehen, wie eine Lösung für dieses Problem durch die Anwendung eines neuen Konzepts der Verwendung mehrerer Typenbezeichner aussehen würde.
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. //+------------------------------------------------------------------+
Code 05
An dieser Stelle wird es wirklich kompliziert, obwohl wir vorsichtig waren und alles nach und nach gezeigt haben. Das Problem liegt hier jedoch genau darin, was wir in Zeile 12 tun, wo das Template für die Summenfunktion deklariert wird.
Nach der Ausführung führt Code 05 zu dem in der folgenden Abbildung dargestellten Ergebnis.
Bild 04
Wir haben einen Punkt in diesem Bild besonders hervorgehoben, um die Aufmerksamkeit des Lesers zu erregen. Bitte beachten Sie, dass das Ergebnis des Vorgangs nicht korrekt ist. Vielmehr gibt es KEINE ÜBEREINSTIMMUNG mit dem erwarteten Wert, da der erwartete Wert hier derselbe wäre wie in Abbildung 03. Aber warum? Wir können davon ausgehen, dass es sich um die Art und Weise handelt, wie Zeile 12 notiert ist, da sie offensichtlich keinen Sinn ergibt. Das Problem ist jedoch NICHT IN ZEILE 12 wie Sie vielleicht vermuten, sondern in Zeile 09 oder 16, je nachdem, wie Sie den Code analysieren oder wie er implementiert werden sollte. Daher ist es wichtig, akzeptierte Konzepte zu verstehen und immer zu überlegen, bevor man wahllos kodiert.
Sie brauchen mir nicht zu glauben. Also, ÄNERN WIR ZEILE 16 NICHT, aber wir werden die Zeile 09 ändern, indem wir die Reihenfolge, in der die Werte deklariert werden, ändern. Dies führt uns zu dem unten stehenden Code.
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. //+------------------------------------------------------------------+
Code 06
Bitte beachten Sie, dass wir nur das geändert haben, was wir beschrieben haben, und das Ergebnis der Codeausführung ist unten zu sehen.
Bild 05
„Was für eine verrücktes und sinnloses Vorgehen! Jetzt habe ich Angst, mit dem Programmieren anzufangen, bis ich das Problem besser verstehe. Das erschien mir alles sehr einfach. Ich hielt mich sogar für einen Programmierer, weil ich in der Lage war, kleine Codeschnipsel zu schreiben, die funktionierten. Aber wenn ich mir das ansehe, stelle ich fest, dass ich immer noch nichts Genaues weiß. Ich fange gerade erst an zu lernen, was es bedeutet, ein echter Programmierer zu sein.“
Beruhigen Sie sich, so schlimm ist es doch gar nicht. Tatsächlich ist nicht immer alles so einfach, wie es auf den ersten Blick scheint, vor allem dann nicht, wenn wir uns in unsere Komfortzone begeben und sie nicht verlassen. Das Problem ist jedoch, dass viele Menschen sich für gute Programmierer halten und deshalb einfach aufhören zu lernen. Daher bestätige ich:
Ein guter Programmierer hört NIEMALS AUF ZU LERNEN. Er aktualisiert ständig sein Wissen und erforscht neue Konzepte. IMMER.
Dinge wie das, was Sie gerade gesehen haben, ZERSTÖREN die Moral eines Programmierers, vor allem, wenn er Code in die Hände bekommt, der im Prinzip korrekt ist. Und sie ist im Wesentlichen richtig. Es liefert jedoch ohne ersichtlichen Grund immer falsche Ergebnisse. Machen Sie sich also nichts vor: Es reicht nicht aus, Code schreiben zu können, um als Programmierer zu gelten. Um dieses Niveau zu erreichen, muss man eine Menge durchmachen. Und in vielen Fällen kann man nur mit der Zeit lernen. Ich habe das selbst durchgemacht, und ich sage Ihnen: Es hat lange gedauert, bis ich gelernt habe, mit solchen Problemen umzugehen. Ich begann sogar zu verstehen, warum der Code in einem Moment funktioniert und in einem anderen verrückt spielt und auf den ersten Blick sinnlose Ergebnisse liefert.
Aber lassen Sie uns herausfinden, was sowohl in Code 05 als auch in Code 06 passiert, denn sie sind dasselbe: Der einzige Unterschied ist eine einfache Änderung in der Reihenfolge der Parameterdeklaration in Zeile 09.
Wenn der Compiler auf den in Zeile 12 deklarierten Template-Aufruf stößt, prüft er wie zuvor, welcher Datentyp in den einzelnen Argumenten verwendet wird, und erstellt eine überladene Funktion für diesen speziellen Aufruf, für den Fall, dass keine der zuvor erstellten Funktionen das aktuelle Template bedienen kann.
Da wir in Zeile 12 zwei typename haben, um zwei verschiedene Typen zu bezeichnen, können wir zwei völlig unterschiedliche Datentypen verwenden, was vorher unmöglich war. Dies deckt eine große Anzahl von Fällen perfekt ab, da die Werte entweder Ganzzahlen oder Fließkommazahlen sein können und wir uns nicht gleich zu Beginn darum kümmern müssen. Aufgrund spezifischer Probleme deckt eine solche Simulation jedoch nicht alle Fälle ab, aber das würde den Rahmen des heutigen Themas sprengen. Da wir wissen, dass wir sowohl Ganzzahl- als auch Fließkommadaten gleichzeitig und ohne Probleme verwenden können, erhalten wir ein nahezu perfektes Template.
So wird der Compiler den im ersten Argument deklarierten Datentyp in die Konstante T1 setzen, während der im zweiten Argument verwendete Datentyp in die Konstante T2 gesetzt wird. Nachdem wir in Zeile 16 eine Typumwandlung durchgeführt haben, um dem von der Funktion Summe zurückgegebenen Typ zu entsprechen, erhalten wir ein Ergebnis, das dem in den Abbildungen 04 und 05 gezeigten ähnelt.
Da wir bei der Betrachtung von Template-Code nur eine vage Vorstellung davon haben, was der Compiler tatsächlich zusammensetzen wird, ist es schwer zu verstehen, warum wir so unterschiedliche Ergebnisse erhalten, nur weil wir die Reihenfolge der Deklaration von Werten in Zeile 09 geändert haben.
Zum besseren Verständnis wollen wir sehen, wie die vom Compiler geschriebene Funktion in beiden Fällen aussieht. Wenn wir davon ausgehen, dass wir kein Template, sondern eine traditionelle Kodierung verwenden, erhalten wir den folgenden Code 05:
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. //+------------------------------------------------------------------+
Code 07
Wenn Sie den Code 07 ausführen, erhalten Sie genau das, was in Abbildung 04 zu sehen ist. Und der Code 06 hätte, wenn er nach einem traditionellen Programmiermodell erstellt worden wäre, als internen Inhalt das gehabt, was unten zu sehen ist.
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. //+------------------------------------------------------------------+
Code 08
So wie Code 07 zu Bild 04 führen würde, würde Code 08 zu Bild 05 führen. Bitte beachten Sie, dass der Unterschied zwischen einem Code und einem anderen sehr subtil ist und in vielen Situationen unbemerkt bleiben kann, sodass wir nicht einmal merken, dass etwas schief gelaufen ist. Im Gegensatz zu den Codes, die Templates verwenden, werden wir bei den Codes 07 und 08 die Ursache des Problems jedoch schnell erkennen. Anhand der Ergebnisse können Sie erkennen, dass hier eine explizite Typisierung vorliegt, die den Code veranlasst, eine fehlerhafte Antwort zu liefern, wodurch das Problem behoben wird.
Bei der Verwendung eines Template ist dies jedoch nicht so leicht zu erkennen. In diesem Fall wird es Ihnen schwer fallen und Sie werden sich vielleicht sogar weigern, diesen Weg weiterzugehen. Und nur wenn Sie willensstark genug sind, werden Sie irgendwann herausfinden, wo der Fehler liegt. Bevor Sie jedoch denken: „Wir wollen also nur explizites Typecasting verwenden, um in Zeile 16 von Code 05 oder Code 06 doppelt zu schreiben. Das würde unser Problem mit der Resonanz im Terminal sicherlich lösen“.
Lassen Sie uns sehen, warum dies das Problem nicht wirklich löst. In einigen Fällen werden dadurch sogar weitere Probleme geschaffen. Und der Grund dafür ist ganz einfach. Das Problem ist, dass Sie versuchen werden, das Ergebnis in Zeile 09 des Codes zu lösen, und dabei vergessen, dass es eine Zeile 08 gibt, in der wir Daten vom Typ Ganzzahl verwenden. Anstatt das Problem zu beheben, können Sie die Situation weiter verwirren und dadurch ein neues Problem an anderer Stelle schaffen.
Das ist sicherlich eine typische Situation, die irgendwann ziemlich unangenehm wird. Daher ist es selten, dass man praktischen Code findet, der ein Template mit mehreren Typen verwendet, zumindest in MQL5. In der Sprache C, vor allem in C++, sind solche Dinge sehr häufig und kommen ständig vor.
Daher ist das, was hier vorgestellt wird, eher interessant als nützlich für den täglichen Gebrauch. Wenn es jedoch notwendig wird, es zu nutzen, werden Sie sich schnell daran erinnern, welche Probleme es verursachen kann. Es steht Ihnen also frei, eine Lösung für einen solchen Konflikt zu finden. Soweit ich weiß, gibt es keine einfache Möglichkeit, dieses Problem zu lösen. Selbst in C++, wo dies der Fall ist, muss man oft aussteigen, um das Problem zu umgehen. Und glauben Sie mir, das ist überhaupt nicht lustig.
Abschließende Überlegungen
In diesem Artikel wurde erläutert, wie man mit einer der ärgerlichsten und schwierigsten Programmiersituationen umgeht, die einem begegnen kann: die Verwendung verschiedener Typen in derselben Funktion oder Prozedur-Template. Obwohl wir uns die meiste Zeit nur mit Funktionen beschäftigt haben, ist alles, was hier behandelt wurde, nützlich und kann auf Prozeduren angewendet werden, sowohl bei der Wertübergabe als auch bei der Referenzübergabe. Um das Material nicht zu langwierig zu machen, werden wir diese Fälle jedoch nicht in Aktion zeigen.
Wir sind uns also einig, dass Sie üben und versuchen sollten, kleine Codefragmente zu formulieren, um die Fälle zu erkunden, über die wir vorhin gesprochen haben, wie z. B. die Arbeit mit Schritt-für-Schritt-Verknüpfungen und die Verwendung von Verfahrens-Templates, denn in diesem frühen Stadium werden wir nicht unbedingt damit arbeiten.
In der Anwendung finden Sie viele der heute angebotenen Codes. Bei den nicht verfügbaren Codes handelt es sich um einfache Änderungen der in der Anwendung enthaltenen Codes. Auf jeden Fall wird das Üben des hier Gezeigten dazu beitragen, den Inhalt besser zu beherrschen. Im nächsten Artikel werden wir mehr über Templates sprechen. Also, bis sehr bald!
Übersetzt aus dem Portugiesischen von MetaQuotes Ltd.
Originalartikel: https://www.mql5.com/pt/articles/15668
Warnung: Alle Rechte sind von MetaQuotes Ltd. vorbehalten. Kopieren oder Vervielfältigen untersagt.
Dieser Artikel wurde von einem Nutzer der Website verfasst und gibt dessen persönliche Meinung wieder. MetaQuotes Ltd übernimmt keine Verantwortung für die Richtigkeit der dargestellten Informationen oder für Folgen, die sich aus der Anwendung der beschriebenen Lösungen, Strategien oder Empfehlungen ergeben.





- Freie Handelsapplikationen
- Über 8.000 Signale zum Kopieren
- Wirtschaftsnachrichten für die Lage an den Finanzmärkte
Sie stimmen der Website-Richtlinie und den Nutzungsbedingungen zu.