Mit dem Schlüsselwort constexpr lässt sich ein Ausdruck definieren, der zur Compilezeit evaluiert werden kann. constexpr lässt sich für Variablen, Funktionen aber auch benutzerdefinierte Typen verwenden. Ein zur Compilezeit evaluierter Ausdruck besitzt viele Vorteile. So sind constexpr Variablen und auch Instanzen von benutzerdefinierten Typen automatisch thread-sicher und können im billigen ROM gespeichert werden, so werden constexpr Funktionen zur Compilezeit ausgeführt, so dass das Ergebnis der Funktion direkt zur Laufzeit zur Verfügung steht.
Alles zur Compilezeit
In dem Artikel Benutzerdefinierte Literale habe ich es bereits angedeutet. Die Berechnung, wie viele Kilometer ich im Schnitt in der Woche mit dem Auto zurücklege, besitzt deutliches Optimierungspotential. Mein Versprechen löse ich mit diesem Artikel ein. Um den Faden wieder leichter aufzunehmen. Das Programm aus dem Artikel Benutzerdefinierte Literale auf einen Blick.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 |
// userdefinedLiterals.cpp #include <iostream> namespace Distance{ class MyDistance{ public: MyDistance(double i):m(i){} friend MyDistance operator+(const MyDistance& a, const MyDistance& b){ return MyDistance(a.m + b.m); } friend MyDistance operator-(const MyDistance& a,const MyDistance& b){ return MyDistance(a.m - b.m); } friend MyDistance operator*(double m, const MyDistance& a){ return MyDistance(m*a.m); |
Wie kann das Programm deutlich optimiert werden? Ganz einfach, durch constexpr. Die zentrale Idee ist es, alle Instanzen der Klasse MyDistance im Hauptprogramm als constexpr zu deklarieren. Dadurch fordere ich vom Compiler, die Objekte zur Compilezeit zu instanziieren. Das kann der Compiler aber nur, wenn die Instanziierung auf konstanten Ausdrücken basiert. Falls dies nicht möglich ist, moniert dies der Compiler sofort.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
// userdefinedLiteralsConstexpr.cpp #include <iostream> namespace Distance{ class MyDistance{ public: constexpr MyDistance(double i):m(i){} friend constexpr MyDistance operator+(const MyDistance& a, const MyDistance& b){ return MyDistance(a.m + b.m); } friend constexpr MyDistance operator-(const MyDistance& a,const MyDistance& b){ return MyDistance(a.m - b.m); } friend constexpr MyDistance operator*(double m, const MyDistance& a){ return MyDistance(m*a.m); } friend constexpr MyDistance operator/(const MyDistance& a, int n){ return MyDistance(a.m/n); } friend std::ostream& operator<< (std::ostream &out, const MyDistance& myDist){ out << myDist.m << " m"; return out; } private: |
Das Ergebnis der Berechnung ist nicht besonders spannend. Das Übersetzen des Programms setzt aber einen Compiler voraus, der den C++14 Standard umsetzt. Mit einem aktuellen GCC- oder clang-Compiler ist dies sichergestellt. Der aktuellen Microsoft Visual 2015 C++-Compiler unterstützt constexpr Funktionen nach dem C++11-Standard. Daher scheitert er an der Funktion getAverageDistance. Im C++11-Standard darf eine constexpr Funktion nur aus einer return-Anweisung bestehen.
Viel spannender ist es da schon, sich die Assembleranweisungen anzuschauen. Am einfachsten geht es mit dem Interaktiven Compiler, der auf https://gcc.godbolt.org/ gehosted ist.
Wie ist das Ergebnis zu interpretieren? Ganz einfach. Die in Hauptprogramm (Zeile 64 - 75) definierten konstanten Ausdrücke sind bereits als Konstanten Teil des Assemblerprogramms. Anders ausgedrückt. Alle Berechnung werden zur Compilezeit ausgeführt. Zur Laufzeit liegen nur noch die konstanten Ausdrücke vor. Einfacher kann es für die Laufzeit nicht mehr sein.
Wie geht's weiter?
So, dass soll als Aperitif ausreichend sein. Die Details folgen im nächsten Artikel. In dem Artikel werde ich mir constexpr Variablen, Funktionen und benutzerdefinierte Typen genauer anschauen. Es gilt einige Dinge im Kopf zu behalten. Zum einen sind constexpr Funktionen in C++14 deutlich mächtiger als in C++11, zum anderen können constexpr Funktionen auch zur Laufzeit ausgeführt werden. Darüber gelten für die Methoden von benutzerdefinierten Typen einige Einschränkungen, wollen sie zur Compilezeit instanziiert werden.
Go to Leanpub/cpplibrary "What every professional C++ programmer should know about the C++ standard library". Hole dir dein E-Book. Unterstütze meinen Blog.
Weiterlesen...