
Zusammenfassung der vorherigen Teile
Aufgrund von Einschränkungen bei der Verwendung von C ++ 11-Compilern und mangelnder Alternativen wollte boost seine eigene Implementierung der Standard-C ++ 11-Bibliothek über die mit dem Compiler gelieferte C ++ 98 / C ++ 03-Bibliothek schreiben.
Static_assert ,
noexcept ,
countof wurden implementiert, und nach Berücksichtigung aller nicht standardmäßigen
Definitionen und Compilerfunktionen wurden Informationen zu den vom aktuellen Compiler unterstützten Funktionen
angezeigt . Eine eigene Implementierung von
nullptr ist enthalten , die bei der Kompilierung ausgewählt wird.
Es ist Zeit für
type_traits und all diese "besondere Vorlagenmagie".
Link zu GitHub mit dem Ergebnis für heute für ungeduldige und Nichtleser:
Engagements und konstruktive Kritik sind willkommen
Tauchen Sie ein in die Welt von "Template Magic" C ++.
Inhaltsverzeichnis
EinführungKapitel 1. Viam Supervadet VadensKapitel 2. #ifndef __CPP11_SUPPORT__ #define __COMPILER_SPECIFIC_BUILT_IN_AND_MACRO_HELL__ #endifKapitel 3. Finden der perfekten nullptr-ImplementierungKapitel 4. C ++ Template Magic....
4.1 Wir fangen klein an....
4.2 Über wie viele wundersame Fehler das Protokoll für uns kompiliert....
4.3 Zeiger und alles in allem....
4.4 Was wird sonst noch für die Vorlagenbibliothek benötigt?Kapitel 5
...
Kapitel 4. C ++ Template Magic
Nachdem ich mit den C ++ 11-Schlüsselwörtern und allen definierungsabhängigen "Schaltern" zwischen ihren Implementierungen
fertig war , begann ich,
type_traits zu füllen. In Wahrheit hatte ich bereits einige Vorlagenklassen, ähnlich den Standardklassen, die bereits lange in Projekten gearbeitet hatten, und daher blieb es, all dies in die gleiche Form zu bringen und die fehlenden Funktionen hinzuzufügen.

Ehrlich gesagt bin ich von der Vorlagenprogrammierung inspiriert. Insbesondere die Erkenntnis, dass dies alles eine Vielzahl von Optionen ist: Berechnungen, Code-Verzweigung, Bedingungen, Fehlerprüfung werden während des Kompilierungsprozesses durchgeführt und nichts kostet das endgültige Programm zur Laufzeit. Und da Vorlagen in C ++ im Wesentlichen eine
Turing-vollständige Programmiersprache sind , war ich gespannt, wie elegant und relativ einfach es möglich sein würde, den Teil des Standards zu implementieren, der mit der Programmierung auf Vorlagen verbunden ist. Um jedoch alle Illusionen sofort zu zerstören, werde ich sagen, dass die gesamte Theorie der Turing-Vollständigkeit in konkrete Implementierungen von Vorlagen in Compilern zerlegt ist. Und dieser Teil des Schreibens der Bibliothek wurde anstelle eleganter Lösungen und „Tricks“ der Vorlagenprogrammierung zu einem heftigen Kampf mit Compilern, während jeder auf seine Weise „zusammenbrach“, und es ist gut, wenn er in einen verwischten internen Compilerfehler geriet oder sogar eng zusammenbrach unbehandelte Ausnahmen. GCC (g ++) zeigte sich am besten, das alle Template-Konstruktionen stoisch „kaute“ und (im Fall) nur an Stellen verfluchte, an denen ein expliziter
Typname fehlte .
4.1 Klein anfangen
Ich habe mit einfachen Vorlagen für
std :: Integral_constant ,
std :: bool_constant und ähnlichen kleinen Vorlagen begonnen.
template<class _Tp, _Tp Val> struct integral_constant {
Basierend auf
Bedingungen können Sie praktische Vorlagen für logische Operationen {"und", "oder", "nicht"} über Typen eingeben (und all diese Operationen werden bereits in der Kompilierungsphase berücksichtigt! Es ist großartig, nicht wahr?):
namespace detail { struct void_type {};
Drei Punkte verdienen hier Aufmerksamkeit:
1) Es ist wichtig, immer ein Leerzeichen zwischen die spitzen Klammern ('<' und '>') der Vorlagen zu setzen, da vor C ++ 11 im Standard keine Klarstellung darüber enthalten war, wie '>>' und '<<' in Code wie _oder _ <_ B2 zu interpretieren sind. _oder _ <_ B3, _B4 >> , und daher haben fast alle Compiler dies als Bitverschiebungsoperator behandelt, was zu einem Kompilierungsfehler führt.
2) In einigen Compilern (z. B. Visual Studio 6.0) gab es einen Fehler, der darin bestand, dass der void- Typ nicht als Vorlagenparameter verwendet werden konnte. Zu diesem Zweck wird im obigen Abschnitt ein separater void_type- Typ eingeführt, um den void- Typ zu ersetzen, für den der Standardwert des Vorlagenparameters erforderlich ist.
3) Sehr alte Compiler (zum Beispiel Borland C ++ Builder) hatten einen schief implementierten Typ- Bool , der in einigen Situationen „plötzlich“ zu int ( true -> 1, false -> 0) wurde, sowie Typen konstanter statischer Variablen des Typs bool (und nicht nur sie), wenn sie in Vorlagenklassen enthalten waren. Aufgrund all dieser Unordnung könnte der Compiler für einen völlig harmlosen Vergleich im Stil von my_template_type :: static_bool_value == false leicht eine Verzauberung ausgeben , die nicht 'undefined type' in int (0) oder ähnliches umwandeln kann . Daher muss immer versucht werden, den Typ der zu vergleichenden Werte explizit anzugeben, damit der Compiler bestimmen kann, um welche Typen es sich handelt.
Fügen Sie mehr Arbeit mit
konstanten und
flüchtigen Werten hinzu. Zunächst das trivial implementierte
remove_ ..., bei dem wir die Vorlage einfach auf bestimmte
Typmodifikatoren spezialisieren. Wenn der Typ mit dem Modifikator in die Vorlage eingeht, muss der Compiler nach Prüfung aller Spezialisierungen (beachten Sie das SFINAE-Prinzip aus dem
vorherigen Kapitel ) der Vorlage die am besten geeignete auswählen (mit expliziter Angabe des gewünschten Modifikators). ::
template<class _Tp> struct is_function; template<class _Tp> struct remove_const {
Und dann implementieren wir
add_- Vorlagen ... wo alles schon etwas komplizierter ist:
namespace detail { template<class _Tp, bool _IsFunction> struct _add_const_helper { typedef _Tp const type; }; template<class _Tp> struct _add_const_helper<_Tp, true> { typedef _Tp type; }; template<class _Tp, bool _IsFunction> struct _add_volatile_helper { typedef _Tp volatile type; }; template<class _Tp> struct _add_volatile_helper<_Tp, true> { typedef _Tp type; }; template<class _Tp, bool _IsFunction> struct _add_cv_helper { typedef _Tp const volatile type; }; template<class _Tp> struct _add_cv_helper<_Tp, true> { typedef _Tp type; }; }
Hier verarbeiten wir die Referenztypen sorgfältig separat, um den Link nicht zu verlieren. Wir werden auch nicht vergessen, welche Arten von Funktionen es
im Prinzip unmöglich macht,
flüchtig oder
konstant zu machen, deshalb werden wir sie "wie sie sind"
belassen . Ich kann sagen, dass dies alles sehr einfach aussieht, aber genau dies ist der Fall, wenn "der Teufel im Detail steckt" oder vielmehr "Fehler im Detail der Implementierung".
Das Ende des ersten Teils des vierten Kapitels. Im
zweiten Teil werde ich darüber sprechen, wie hart die Vorlagenprogrammierung dem Compiler gegeben wird, und es wird auch mehr coole Vorlagenmagie geben. Ah, und doch - warum ist
Long Long laut einigen Compilern bis heute keine
integrale Konstante ?
Danke für die Aufmerksamkeit.