Der erste Artikel zu den für C++20 geplanten Concepts hat bereits gezeigt, wie man mit Klassen- und Funktions-Concepts C++-Code stärker abstrahieren kann. Auch ihre Syntax lässt sich noch weiter vereinfachen.
Die Geschichte zu Concepts in C++20 geht weiter. Dieser Artikel zeigt, wie sich die Syntax für Templates, Concepts und Platzhalter stärker vereinfachen lässt. Außerdem geht er der Frage nach, inwiefern man Haskells Typklassenhierarchie in C++ mit Concepts modellieren kann.
Um den roten Faden fortzuführen: Im ersten Artikel zu diesem Thema auf Seite 48 ist zu lesen, dass sich eingeschränkte Platzhalter (Concepts) überall dort einsetzen lassen, wo auch uneingeschränkte Platzhalter (auto) erlaubt sind.
Hier soll es jetzt „süß“ weitergehen, nämlich mit Syntactic Sugar, syntaktischen Ausdrücken, die das Lesen einer Programmiersprache erleichtern (siehe dazu den Link unter ix.de/ix171054). Die folgenden Ausführungen zeigen, wie man Platzhalter einfacher anwenden kann. In bekannter Manier kommt in Listing1 der GCD-Algorithmus (Greatest Common Divisor) zusammen mit dem Concept Integral zum Einsatz, um ihn typsicher zu machen.
Die Zeilen4 bis7 enthalten das aus dem ersten Artikel bekannte Concept Integral, das in den Zeilen9 bis 31 mehrfach eingesetzt wird. Dabei wird die Syntax immer einfacher. Anstatt in Zeile 18 das Concept in der requires-Klausel (Zeile 10) zu verwenden, kommt es dort anstelle des gewohnten Schlüsselworts typename oder class für den Typparameter zum Einsatz. Es wird noch „süßer“. In Zeile 26 dient das Concept Integral als Funktionsparameter. Durch das Concept wird gcd2 zu einem Funktions-Template.
Weiter geht es mit der Vereinheitlichung. Falls ein uneingeschränkter Platzhalter (auto) in Zeile 33 den eingeschränkten Platzhalter (Integral) in Zeile 26 ersetzt, resultiert ein uneingeschränktes Funktions-Template. Der neue Ausdruck „uneingeschränktes Funktions-Template“ steht dafür, dass gcd3 ein Template ist, das Werte beliebigen Typs annehmen kann.
Abbildung1 zeigt die Ausgabe des Programms. Man muss es mit mindestens einem GCC 6.3 und dem Flag –fconcepts übersetzen.
Feinheiten und Überladen von Funktionen
Das Funktions-Template gcd3 in Listing1 ist mächtiger als das Funktions-Template gcd2. gcd3 besitzt zwei Typparameter, die verschieden sein dürfen. Diese Aussage gilt nicht für das Funktions-Template gcd2. Dort müssen a und b vom selben Typ sein, und den muss das Concept Integral selbstverständlich akzeptieren. Die Anwendung des Funktions-Templates twoTypes in Listing2 macht diesen Punkt klar.
Mit ein wenig Hilfe von Run Time Type Information (RTTI) in den Zeilen 22 und 23 ermitteln beide Zeilen die String-Repräsentation der Variablen a und b. Wie erwartet zeigt die Ausgabe des Programms in Abbildung2 die Ergebnisse int und double.
Die Abbildung zeigt auch schön das Zusammenspiel von konkreten Datentypen und Platzhaltern beim Überladen von Funktionen. In den Zeilen 31, 32 und 33 ruft Listing2 die Funktion overload mit jeweils dem Datentyp double, int und long int auf. Der Compiler zieht den konkreten Datentyp long int (Zeile 17) dem eingeschränkten Platzhalter Integral (Zeile 13) vor. Letzteren zieht er wiederum dem uneingeschränkten Platzhalter auto (Zeile9) vor. Die Auswahl der passenden Überladung vollzieht der Compiler daher gemäß seiner Strategie: vom Speziellen zum Allgemeinen. (Siehe dazu den Kasten „Teilweise Template-Spezialisierung“.)
Wer die klassische Art und Weise, Templates zu deklarieren, nicht mag, dem bietet C++20 eine „süßere“ Form an. Statt ein Template in der Form template<Integral T> zu deklarieren, lässt es sich mit „Template Introduction“ direkt durch Integral{T} ausdrücken. Diese vereinfachte Syntax mit geschweiften statt eckigen Klammern ist nur für eingeschränkte Platzhalter (Concepts) zulässig.