C++0x: Ausblick auf den neuen C++-Standard
Seite 6: Lambda-Funktionen
Lambda-Funktionen sorgen fĂĽr Ăśbersicht
C++ erlaubt es zudem bisher nicht, den Typ einer Expression zu erfragen. Der decltype-Operator soll das ändern. Gerade für die Autoren generischer Bibliotheken ist es oft unmöglich, den Rückgabewert einer generischen Funktion fest anzugeben. Da es bisher keine Sprachunterstützung für diesen Fall gab, entstanden Hilfslösungen, die aber durch die Bank gewisse Nachteile haben, sowohl für die Ersteller von Bibliotheken als auch deren Nutzer.
Als Lambda-Funktionen (oder Closures) bezeichnet man unter anderem lokale unbenannte Funktionen. Mittlerweile bieten viele Programmiersprachen hierfĂĽr UnterstĂĽtzung, Beispiele sind Java (mit inner Classes), C# 3.0, Python und so weiter. Der Einsatz von Lambda-Funktionen macht den Code ebenfalls ĂĽbersichtlicher und kompakter. Typische Anwendungsbeispiele sind Classback-Funktionen fĂĽr die GUI-Programmierung (beispielsweise in Java-Swing).
static assertions erlauben dem Programmierer bereits zur Kompilierzeit, Zusicherungen zu machen. Ihre Ursprünge sind in der Boost-Utility-Klasse BOOST_STATIC_ASSERT zu finden, denn dort werden sie zum Beispiel innerhalb der Template-Bibliotheken verwendet. Bisher unterstützt C++ nur zwei einfache Arten der Zusicherung: das Assert-Makro, dessen Evaluierung zur Laufzeit stattfindet, und die #error-Directive des Präprozessors. Da die Auswertung des einfachen Assert erst zur Laufzeit erfolgt, ist es für Template-Parameter-Assertion ungeeignet. Gleiches gilt für die Lösung mit dem Präprozessor, da erst der Compiler die Templates instanziiert und nicht schon der Präprozessor.
C++0x übernimmt weitgehend das Erbe der Boost Static Assertion Library und bietet einen komfortablen Funktionsumfang, zum Beispiel die Verwendung von static assertions auf verschiedenen Ebenen (Block, Klasse, Namensraum et cetera). Da die Überprüfung während des Kompiliervorgangs erfolgt, gibt es keinerlei Runtime-Overhead, aber gegebenenfalls eine aussagekräftige Fehlermeldung. Für Benutzer ist die Notation einfach erlernbar und bietet Schutz gegen falsche Verwendung („foolproof interface“). Listing 4 zeigt den Einsatz auf verschiedenen Ebenen.
Schon lange gibt es in C und C++ den extern-Qualifier. Er teilt dem Compiler mit, dass er keine Instanziierung der Funktion beziehungsweise eines Typs in dieser Übersetzungseinheit vornehmen soll, sondern dass dies in einem anderen Modul erfolgt. Der Linker soll später die betroffenen Stellen miteinander verbinden. Für Templates wurde extern bereits vor C++0x eingeführt. Gerade Templates sind historisch für längere Kompilierungszeiten und den sogenannten Code Bloat bekannt. Um dies zu vermeiden, hatte man extern template eingeführt, ohne gleichzeitig die genaue Semantik zu definieren. Daher hat nur ein kleiner Teil von Compiler-Herstellern dieses Feature implementiert.
C++0x will hier für Verbesserung sorgen. extern template erlaubt nun zusätzlich die Kontrolle darüber, wo der Compiler ein Template instanziiert, was die angesprochenen Probleme vermeidet. Listing 5 zeigt die Unterschiede. Bisher wird in C++ bei der Verwendung eines Template implizit eine Instanziierung vorgenommen. Im gezeigten Fall ist diese unnötig, da sie schon in der Datei Supplier.cpp erfolgt ist und allen Clients zur Verfügung steht. Bei Verwendung des Template im Client Consumer.cpp erfolgt aber ebenfalls eine Instanziierung. C++0x erlaubt es nun, per extern template-Anweisung den Compiler zu veranlassen, die Instanziierung zu unterlassen. Vorteile bringt dieses Sprachkonstrukt sowohl für Bibliotheks-Ersteller und -Nutzer als auch für die generische Programmierung.
Ob die Bereiche Concurrency und Garbage Collection in C++0x aufgenommen werden, steht momentan noch nicht 100 %ig fest, das Standard-Komitee strebt dies aber auf jeden Fall an. Weiterhin gilt das Prinzip „do not pay for what you don’t use´, sodass Garbage Collection auf jeden Fall optional sein wird. Gerade bei hardwarenaher Systemprogrammierung will man so wenig Overhead wie möglich. Hans Boehm von HP treibt dieses Thema voran und bietet über seine Homepage auch eine Gargabe-Collection-Implementierung an. In früheren Zeiten gab es für C++ bereits kommerzielle GC-Produkte (etwa GreatCircle von Geodesic Systems, jetzt Symantec). C++0x will keinesfalls C# oder Java nachahmen, der Hintergrund ist vielmehr, dass mit dem Aufkommen von Mehrprozessorsystemen für die Verwaltung von Shared Ressources GC notwendig werden kann.
Mit der Verbreitung von Multiprozessor-Maschinen und Multithreaded-Programmierung gewinnt der Bereich Concurrency innerhalb von C++ ebenfalls an Bedeutung. Mittlerweile existieren Vorschläge für ein „Memory Model for multithreaded C++“. Gleichzeitig will man eine Standard Library für Thread-Programmierung zur Verfügung stellen. Ob beide Themen mit in C++0x einfließen, ist momentan noch unklar, da sie recht komplex sind.
Ein weiterer wichtiger Punkt ist die Synchronisation von C++0x mit dem aktuellen C99-Standard. Zum einen soll der C++0x-Präprozessor seinem C99-Äquivalent angeglichen werden (Universal Character Names, Standard Pragmas und vordefinierte Makros, Synchronisation von Begriffen etc.). Weiterhin werden der 64-Bit-Typ long long sowie weitere „extended integer types“ Eingang in C++0x finden. Diese Änderungen hat das Komitee schon akzeptiert und weitgehend abgeschlossen.
Die Behebung von Sprachdefekten steht ebenfalls auf der To-Do-Liste des Komitees. Momentan spielt die Zahl 0 eine doppelte Rolle: erstens als Integer-Konstante und zweitens als Null-Pointer. Zumindest syntaktisch behalf man sich bisher mit der Konstante NULL. Oft ist dies aber einfach ein #define auf 0, was das Problem nur kaschiert hat. Sobald man überladene Funktionen in C++ verwendet (siehe Listing 6), können Konstellationen entstehen, in denen man den Aufruf einer bestimmten Funktion nur durch explizite Casts oder über die Definition einer Variablen entsprechenden Typs erreichen kann.
C++0x wird für den Null-Pointer ein neues Schlüsselwort nullptr einführen, über das sich solche Zeiger mit anderen vergleichen lassen. Bei Integer-Werten hingegen ist ein Vergleich oder gar eine Zuweisung nicht erlaubt. Wünschenswert auf längere Sicht wäre es, wenn die Interpretation von 0 als Null-Pointer als veraltet (deprecated) gälte, was aber bei der Menge an existierendem Code (der 0 beziehungsweise NULL verwendet) eher ein Wunschtraum sein dürfte. Die Diskussion über dieses Thema ist wohl ähnlich alt wie die über TRUE/FALSE (dort half ebenfalls bool als Typ).