Kapitel 3. Kernsprache

In diesem Kapitel:

Vergleichen wir C++ mit einer modernen Interpreter-Sprache wie Python oder einer Compiler-Sprache wie Java, sind die Hürden, um die Sprache zu meistern, die es in C++ zu überwinden gilt, viel höher. Klar, der Vergleich ist ungerecht, muss sich doch Python nicht mit statischer Typisierung auseinandersetzen und verfolgt Java doch relativ streng die objektorientierte Programmiertechnik. Aber die Hürden sind mit C++11 deutlich niedriger geworden als mit C++. Die Usability steht bei C++11 im Fokus.

Usability

Ein kleines, aber feines Feature ist die Range-basierte For-Schleife, die das Iterieren über Container deutlich einfacher von der Hand gehen lässt. Sie ist dem einen oder anderen sicher aus Python oder Java schon bekannt.

Wird diese Schleife mit dem Schlüsselwort auto kombiniert, lässt sich sehr kompakt über die Elemente eines C-Arrays, der Standard Library Container oder auch einer Initialisiererliste iterieren (Listing 3.1). Auch wenn die automatische Typableitung mit auto und die praktische Initialisierung eines Containers mit Initialisiererlisten noch nicht dargestellt wurden, sollte sich ihre Anwendung intuitiv erschließen.

rangeBasedForLoop.cpp

Werden die Elemente des C-Arrays oder STL-Containers als Referenzen angenommen, können die Elemente direkt modifiziert werden (Listing 3.1, Zeilen 12 und 18). Selbst das Iterieren über ein std::map geht schnell von der Hand. Sehr beeindruckend ist es, die neue C++11-Syntax (Zeile 33)

for ( auto mapIt: phonebook) std::cout << mapIt.first << ": "
<< mapIt.second << std::endl;

der klassischen C++-Syntax gegenüberzustellen:

std::map <std::string,std::string>::iterator mapIt;
for (mapIt= phonebook.begin();mapIt!= phonebook.end();++mapIt){
     std::cout << mapIt->first << ": " <<
                   mapIt->second << std::endl;
}

Nun fehlt noch die Ausgabe des Programms.

Wie versprochen, wird das erste Geheimnis zum neuen Schlüsselwort auto in C++11 aufgelöst.

Das automatische Ableiten von Typen, bisher vor allem aus funktionalen Sprachen wie Haskell oder Scala bekannt, verbindet die dynamische Typisierung einer Interpreter- mit der statischen Typisierung einer Compiler-Sprache. Dafür führt C++11 zwei neue Schlüsselwörter ein, auto und decltype.

auto und decltype

Der feine Unterschied ist, dass auto den Typ automatisch aus einem Initialisierer ableitet, während decltype einen Ausdruck benötigt, um den Typ zur Übersetzungszeit zu ermitteln. Dabei ist diese automatische Typableitung (type inference) deutlich mehr als syntactic sugar – können doch Rückgabewerte von Templates so komplex sein, dass es nicht trivial ist, den richtigen Typ zu spezifizieren.

auto und decltype

Mit auto oder auch decltype kann schnell eine neue Variable, Referenz oder auch ein Iterator auf einen Container der Standard Template Library definiert werden.

In Listing 3.2 habe ich als Erstes die aktuell gültige C++98-Syntax verwendet. Es folgt die zukünftige C++11-Syntax, zuerst mit decltype in Listing 3.3 und anschließend mit auto in Listing 3.4.

Es wird noch mächtiger. Um eine anonyme Funktion oder auch eine Lambda-Funktion in einer Variablen zu speichern, muss in klassischem C++ ein Funktionszeiger definiert werden. In C++11 reduziert sich die ganze Schreibarbeit auf das Schlüsselwort auto.

myAdd.cpp

Neben auto enthält das Listing noch ein weiteres neues Feature von C++11. Die Lambda-Funktion [](int a, int b){return a + b;} nimmt zwei natürliche Zahlen a und b an, addiert sie und gibt das Ergebnis zurück (Abbildung 3.2).

Lambda-Funktionen sind eine Anleihe aus der funktionalen Programmierung. Da sie Funktionen ohne Namen sind, werden sie auch gern anonyme Funktionen genannt.

Die Struktur einer Lambda-Funktion ist schnell erklärt.

[]()optional → optional {}

Streng genommen sind Lambda-Funktionen lediglich syntactic sugar in C++11, kann mit ihnen doch nichts ausgedrückt werden, was mit klassischem C++ nicht schon möglich wäre. Richtig eingesetzt, erhöhen sie aber deutlich die Lesbarkeit des Codes, da durch sie die Funktionalität genau auf den Punkt gebracht wird. Beispiel gefällig?

In Listing 3.6 wird ein Vektor von Strings sortiert, wobei das Sortierkriterium die Länge der Strings ist. Das erste Sortierkriterium ist die Funktion lessLength, die an die Sortierfunktion in Zeile 26, std::sort(myStrVec.begin(),myStrVec.end(),lessLength), übergeben wird. Das Funktionsobjekt GreaterLength in Zeile 11 kommt in der nächsten Sortierroutine in Zeile 31 zum Einsatz. Die Lambda-Funktion in Zeile 36 bringt es ohne Definition einer Funktion oder eines Funktionsobjekts direkt auf den Punkt.

Die Details rund um Funktionsobjekte sind im Anhang B, genauer erklärt.

mySort.cpp

01 #include <algorithm
02 #include <iostream>
03 #include <iterator>
04 #include <string>
05 #include <vector>
06
07 bool lessLength(const std::string& f, const std::string& s){
08   return f.length() < s.length();
09 }
10
11 class GreaterLength{
12   public:
13     bool operator()(const std::string& f,
                       const std::string& s) const{
14       return f.length() > s.length();
15     }
16 };
17
18 int main(){
19
20   // initializing with a initializer lists
21   std::vector<std::string> myStrVec=
       {"12345","123456","1234","1","12","123","12345"};
22
23   std::cout << "\n";
24
25   // sorting with the function
26   std::sort(myStrVec.begin(),myStrVec.end(),lessLength);
27   std::copy(myStrVec.begin(),myStrVec.end(),
       std::ostream_iterator<std::string>(std::cout, " "));
28   std::cout << "\n";
29
30   // sorting with the function object
31   std::sort(myStrVec.begin(),myStrVec.end(),
       GreaterLength());
32   std::copy(myStrVec.begin(),myStrVec.end(),
       std::ostream_iterator<std::string>(std::cout, " "));
33   std::cout << "\n";
34
35   // sorting with the lambda function
36   std::sort(myStrVec.begin(),myStrVec.end(),
       [](const std::string& f,const std::string& s)
       {return f.length() < s.length();});
37   std::copy(myStrVec.begin(),myStrVec.end(),
       std::ostream_iterator<std::string>(std::cout, " "));
38   std::cout << "\n";
39
40   // using the lambda function for output
41   std::for_each(myStrVec.begin(), myStrVec.end(),
       [](const std::string& s {std::cout << s << ",";});
42
43   std::cout << "\n\n";
44
45 }
Listing 3.6 Sortieren mit einer Funktion, einem Funktionsobjekt und einer Lambda-Funktion

Noch ein paar Worte zur Ausgabe des Programms auf der Konsole. Durch std::copy ist es möglich, die Ausgabe direkt nach std::cout zu kopieren (Zeile 32). Das geht mit Lambda-Funktionen einfacher. In Zeile 41 benutze ich std::for_each, um die Strings direkt nach std::cout zu schreiben.

Die Ausgabe des Programms zeigt die sortierten Strings.

Dem aufmerksamen Leser wird die sehr kompakte Definition eines Vektors myStrVec={"12345","123456",..., "12345"} in Listing 3.6 nicht entgangen sein. Durch Initialisiererlisten wird das Initialisieren von Datentypen nicht nur für den C++-Novizen deutlich einfacher in C++11. Das moderne C++ hat einiges rund um die vereinheitlichte Initialisierung zu bieten.

Vereinheitlichte Initialisierung

Strukturen, Arrays und Container

Die Initialisierung von Objekten in klassischem C++ setzt einiges an Wissen voraus, gibt es doch viele verschiedene Arten, diese zu initialisieren. So lassen sich die C-Strukturen struct sowie Arrays über Initialisiererlisten initialisieren (Listing 3.7, Zeilen 7 und 10), C++-Container der Standard Template Library aber nicht. Als Alternative bietet es sich an, jedes Element beim std::vector einzeln (Listing 3.7, Zeilen 14 bis 18) oder die Elemente indirekt über ein Array (Listing 3.7, Zeile 21) zu initialisieren.

Konstante Element- und Heap-Arrays

Es wird noch komplizierter. C++ kann kein Array myData als Datenelement und kein konstantes Heap-Array pData initialisieren (Listing 3.8), da dafür keine Syntax existiert.

{}-Initialisiererlisten

Hier räumt C++11 auf. C++11 erlaubt {}-Initialisiererlisten für alle Initialisierungen. Damit ist die Initialisierung von Strukturen, Arrays und Containern vereinheitlicht, und Element- sowie konstante Heap-Arrays lassen sich in C++11 einfach initialisieren.

uniformInitialisation.cpp

Hier stellt sich natürlich jetzt die Frage, was ein Datentyp bieten muss, damit er mit Initialisiererlisten initialisiert werden kann. Diese Frage führt uns direkt zum nächsten Kapitel.

Der Entwurf von Klassen wird in C++11 viel mächtiger und expliziter. Einerseits gibt es neue Features rund um die Definition von Konstruktoren, andererseits können Methoden mit Bezeichnern annotiert werden, sodass der Compiler dies prüft.

Neben der Initialisiererliste für Konstruktoren, die wir im letzten Kapitel in Aktion gesehen haben, unterstützt C++11 jetzt auch deren Delegation und Vererbung. Aber nicht nur der Umgang mit Konstruktoren ist mächtiger, einfacher und mit weniger Schreibaufwand verbunden, auch das direkte Initialisieren von Klassenelementen ist jetzt möglich.

Initialisiererliste-Konstruktor

Initialisiererliste-Konstruktoren sind der Widerpart zu den Initialisiererlisten in Listing 3.9. Das Schöne ist, dass die Container der Standard Template Library diese speziellen Konstruktoren schon definiert haben, sodass ein Vektor über eine Initialisiererliste direkt initialisiert werden kann. Aber auch eigene Datentypen lassen sich mit diesen Konstruktoren einfach ausstatten (Listing 3.10).

initializerListConstructor.cp

Sowohl ein String (Zeile 19) als auch der Standardcontainer std::map (Zeile 26) in Listing 3.10 lassen sich über eine Initialisiererliste initialisieren. Das Klassen-Template MyContainer in Zeile 6 nimmt als Argument eine Initialisiererliste von Ganzzahlen und Strings (Zeilen 35 und 39) an. Im Initialisiererliste-Konstruktor von MyContainer (Zeile 9) wird die Initialisiererliste direkt ausgegeben. Hier sehen Sie eine generische Range-basierte For-Schleife im Einsatz. In Kombination mit auto lässt sich so äußerst kompakt über STL-Container iterieren.

Nun fehlt nur noch die Ausgabe des Programms.

Delegation von Konstruktoren

Java oder auch D kennen die Delegation von Konstruktoren. C++ führt sie mit C++11 ein.

Besitzt in klassischem C++ eine Klasse mehrere Konstruktoren, die ähnliche Initialisierungsschritte ausführen müssen, gibt es zwei Lösungen. Die naheliegende Lösung ist, den Initialisierungscode in jedem Konstruktor zu duplizieren. Dies ist fehleranfällig und mit viel Schreibaufwand verbunden. Da ist es schon deutlich besser, eine private Methode init zu definieren, in diese den gemeinsamen Code auszulagern und die Initialisierungsmethode in jedem Konstruktor aufzurufen.

Im neuen C++11 kann der Initialisierungscode in einem Konstruktor definiert werden, der dann von allen anderen Konstruktoren verwendet wird.

delegatingConstructor.cpp

Der Konstruktor MyHour(int hour) (Listing 3.11, Zeile 9) validiert seinen Eingabewert. Daher können die zwei folgenden Konstruktoren in Zeile 16 und 19 ihre Validierung der Daten direkt an diesen durch MyHour():MyHour(0) bzw. MyHour(double hour):MyHour(...)delegieren.

Diese Delegation von Konstruktoren ist schön in Abbildung 3.5 in Anwendung zu sehen.

Vererbung von Konstruktoren

Das Vererben von Konstruktoren erspart einige Schreibarbeit. Ein einfaches using Base::Base in der Definition der Klasse Derived (Listing 3.12) reicht aus, und alle Konstruktoren der Basisklasse stehen in der abgeleiteten Klasse zur Verfügung.

inheritingConstructor.cpp

Direktes Initialisieren der Klassenelemente

Aber nicht nur das Initialisieren mithilfe des Konstruktors, auch das direkte Initialisieren der Klassenelemente wird in modernem C++ unterstützt. Konnten in C++98 nur statische, konstante Elemente integralen Typs initialisiert werden, so gilt die Einschränkung in C++11 nicht mehr. Diese Einschränkung stellte sicher, dass die Initialisierung zur Übersetzungszeit möglich war. Wird ein Klassenelement sowohl direkt als auch über den Konstruktor initialisiert, wird nur Letzteres angewandt.

Ein paar Beispiele zeigen die neue Funktionalität. Der Einfachheit halber verwende ich den Datentyp struct, da hier alle Klassenelemente öffentlich sind.

classMemberInitializer.cpp

Die Ausgabe des Programms zeigt die erwarteten Werte.

Rund um die Initialisierung von Objekten und Klassenelementen gibt es viele neue Features; so können Methoden in C++11 mit Bezeichnern annotiert werden, die das Verhalten der Methode explizit beschreiben.

Wieso sollte Pythons Designprinzip »Explicit is better then implicit.« von Tim Peters (Peters, 2004) nicht auch für C++11 gelten?

Methoden können in modernem C++ mit den Bezeichnern default, delete, override, final oder auch explicit ausgezeichnet werden. Der Compiler sorgt dafür, dass der Vertrag eingehalten wird.

default und delete

Für eine Klasse werden viele spezielle Methoden vom Compiler erzeugt, um den Lebenszyklus seiner Instanzen zu gewährleisten. Dies betrifft den Standard- und den Kopierkonstruktor, den Zuweisungsoperator und den Destruktor. Aber auch spezielle Methoden wie operator new werden vom Compiler bei Bedarf erzeugt.

Für C++-Entwickler gibt es viele Idiome in klassischem C++, um den Lebenszyklus eines Objekts direkt zu kontrollieren. Diese Idiome setzen viel Wissen und noch mehr Disziplin voraus. Mit den Bezeichnern default und delete hat sich der C++-Standard dieser Problematik angenommen.

Soll eine Klasse zum Beispiel nicht kopierbar sein, werden der Kopierkonstruktor und der Zuweisungsoperator lediglich privat deklariert, aber nicht definiert. Hier setzt der delete-Bezeichner an. Steht delete hinter dem Methodennamen, wird die Methode vom Compiler nicht mehr erzeugt.

Aber es lauern auch Gefahren rund um den Lebenszyklus eines Objekts. Wird in einer Klasse ein Konstruktor definiert, erzeugt der Compiler keinen Standardkonstruktor mehr. Genau dies kann der Klassendesigner dem Compiler jedoch durch den Bezeichner default vorschreiben. default sorgt dafür, dass der Compiler seine Default-Version der Methode erzeugt.

Der Lebenszyklus eines Objekts lässt sich in C++11 rein deklarativ beschreiben.

defaultedDeletedMethods.cpp

Vor der Übersetzung des Programms aus Listing 3.14 noch ein paar Worte zum Sourcecode:

In der Klasse NonCopyableClass (Zeile 3) werden sowohl der Kopierzuweisungsoperator als auch der Kopierkonstruktor als delete erklärt, und der Standardkonstruktor des Compilers wird verwendet. Der Standardkonstruktor wird in der Klasse NonCopyableClass nicht automatisch vom Compiler erzeugt, da der Kopierzuweisungsoperator und der Kopierkonstruktor als delete deklariert wurden. Damit lassen sich Objekte der Klasse nur direkt instanziieren. Auch die Klasse SomeType (Zeile 15) nutzt den vom Compiler erzeugten Standardkonstruktor, den der Compiler in diesem Fall nicht erzeugt, da ein spezieller Konstruktor für int vorhanden ist. Interessant ist auch die Klasse TypeOnStack (Zeile 26), denn das Setzen des new-Operators auf delete bewirkt, dass deren Instanzen nicht mehr mit new erzeugt werden können. Der interessanteste Teil des Programms ist aber die main-Funktion, denn in ihr wird sowohl ein nicht kopierbares Objekt kopiert als auch ein Objekt auf dem Heap angelegt, obwohl dessen new-Operator auf delete gesetzt ist.

Wie geht der GCC-Compiler mit dem Vertragsbruch um?

Der GCC schreibt eine aussagekräftige Fehlermeldung. Was will man mehr?

Explizite Virtualität

Es bleibt deklarativ. Eine beliebte Fehlerquelle beim Überschreiben von virtuellen Funktionen ist, dass die Signatur der neuen Methode nicht der der zu überschreibenden Methode entspricht. Das Ergebnis zeigt sich erst sehr viel später zur Laufzeit, wenn sich das Programm unerwartet verhält. Diese Fehlerquelle lässt sich mit C++11 elegant beseitigen. Wird die neue Funktion mit dem Schlüsselwort override versehen, stellt der Compiler sicher, dass diese auch tatsächlich eine Methode der Basisklasse überschreibt. Der Compiler stellt ebenfalls sicher, dass Methoden, die als final deklariert sind, nicht überschrieben werden können.

Vom Novizen zum Profi

Bisher hatte C++11 viel für den C++-Novizen zu bieten: das automatische Ableiten von Typen, die vereinheitlichte Initialisierung von Datentypen, die mächtigere Initialisierung von Objekten, die deklarativen Klassendefinitionen. Selbst Lambda-Funktionen sind, ist das erste Fremdeln einmal überwunden, leicht zu schreiben und zu lesen. Nun ist der Profi an der Reihe. In den beiden folgenden Abschnitten „Rvalue-Referenzen“ und „Generische Programmierung“ findet dieser die Werkzeuge, die er braucht, um seine Datentypen und Bibliotheken genau auf seine Bedürfnisse abzustimmen.

Rvalue-Referenzen

Zwei spezielle Methoden sind im Abschnitt „Mächtigere Initialisierung“ nicht genannt worden: der neue Move-Konstruktor und der Move-Zuweisungsoperator, den die STL-Container und auch der Datentyp String besitzen.

Betrachten wir die vereinfachte Implementierung des std::vector-Containers in Listing 3.15, fällt auf, dass neben dem klassischen Kopierkonstruktor (Zeile 6) und dem Zuweisungsoperator (Zeile 7) zwei sehr ähnliche neue Methodendeklarationen in Zeile 10 und 11 existieren. Der auffälligste Unterschied ist, dass die traditionellen Methoden ihre Argumente als eine konstante Lvalue-Referenz mit einem & annehmen, während die neuen Methoden ihre Argumente als Rvalue-Referenz mit && annehmen.

Beides sind Referenzen im klassischen C++-Sinn. Der C++-Compiler entscheidet aber, welche Implementierung bei der Konstruktion oder auch Zuweisungsoperation verwendet wird, denn mit Lvalue-Referenzen wird die klassische und bekannte Copy-Semantik implementiert, mit Rvalue-Referenzen die neue Move-Semantik.

Verwirrt? Verständlich! Das kleine Listing 3.15 soll für Aufklärung sorgen.

copyMoveSemantic.cpp

Die neue C++11-Funktion std::move in Zeile 31 hat zur Folge, dass der String str1 als Rvalue vom C++-Compiler interpretiert wird. Damit wird der Inhalt von str1 nach str3 verschoben.

Die nächsten zwei Abbildungen stellen den Unterschied zwischen der Copy- und der Move-Semantik dar. Während nach dem Kopieren sowohl die Quelle str1 als auch das Ziel str2 den gleichen Inhalt besitzen, ist die Quelle str1 nach dem Verschieben des Inhalts leer.

Die Ausgabe des Programmlaufs in Abbildung 3.10 zeigt die Ergebnisse auf der Konsole.

Die Grundlage für diese Optimierungen sind die Rvalues. Anhand dieser kann der C++-Compiler entscheiden, welche Implementierung der Methode verwendet werden soll.

Perfect Forwarding

Wie der C++-Entwickler eigene Datentypen entwirft, die mit Move-Semantik ausgestattet sind, wird in Teil II erläutert. Dies gilt auch für das Perfect Forwarding, bei dem Argumente an eine andere Funktion weitergegeben werden, ohne ihre Lvalue- oder Rvalue-Eigenschaft zu verändern. Ein bisher ungelöstes Problem in C++.

Weiter geht es mit neuen Features für den C++-Profi. Das Programmieren mit Templates wird deutlich mächtiger in C++11.

Generische Programmierung

Das generische Programmieren, auf dem die Standard Template Library basiert, ist in C++ ein wichtiges Paradigma. Daher verwundert es nicht, dass C++11 hier einiges Neues zu bieten hat:

  • Templates, die beliebig viele Parameter annehmen,

  • Zusicherungen, die zur Compile-Zeit ausgewertet werden,

  • Konstanten, die zur Compile-Zeit evaluiert werden,

  • Aliase Templates, um einfache Namen für teilweise gebundene Templates zu definieren.

Variadic Templates

Variadic Templates

Variadic Templates in C++11 erlauben es, Templates zu schreiben, die beliebig viele Argumente annehmen können. Ein prominentes Beispiel für den Einsatz von Variadic Templates ist der neue heterogene sequenzielle Datentyp std::tuple, der eine beliebige Länge besitzen kann.

Da die neue Syntax der Variadic Templates doch recht ungewohnt wirkt, zuerst ein einfaches Beispiel. Das Funktions-Template countMe in Listing 3.17 zählt die Anzahl seiner Argumente.

countMe.cpp

Ungewohnt an dem Funktions-Template countMe sind zuallererst die drei Punkte ... Dabei gilt es, aufmerksam darauf zu achten, ob diese links (<typename ... Args>, Zeile 4) oder rechts ((Args ... args), Zeile 5) von Args stehen. Links packt der Ellipsenoperator ... das sogenannte Parameter Pack, rechts entpackt er es wieder. Neu ist auch der Operator sizeof ... (Zeile 6), der direkt mit Parameter Packs umgehen kann.

Jetzt fehlt nur noch die Ausgabe des Programms.

Das Programm countMe in Listing 3.17 ist aber nicht der klassische Anwendungsfall für Variadic Templates. Deutlich typischer ist das folgende Muster aus der funktionalen Programmierung, wenn es um die Verarbeitung beliebig langer Listen geht.

Dazu wird die Liste in die zwei Teile first und rest getrennt, wobei

  • first das erste Element der Liste und

  • rest den Rest der Liste

bezeichnet.

Für beide Bereiche der Liste werden zwei Aktionen registriert.

  1. Aktion (first): Verarbeite first.

  2. Aktion (rest): Verarbeite first und führe Aktion (rest) auf der verbleibenden Liste aus.

Der Trick ist, dass rest bei jeder Rekursion um das erste Element first gekürzt wird.

Genau diesem Muster folgt das Programm in Listing 3.18. Als Aktion auf dem Kopf der Liste first werden dessen Typinformation und Größe herausgegeben.

printValueInfo.cpp

In Zeile 38 wird die Aktion printValueInfo auf der ganzen Liste angestoßen. Die Liste besitzt mehr als ein Element, sodass das Funktions-Template printValueInfo(First first, Rest ... rest ) in Zeile 22 aufgerufen wird. Dabei wird true an first gebunden, und die verbleibenden Argumente werden an rest gebunden. true wird über die Hilfsfunktion printInfoFor (Zeile 5) ausgegeben, und rest wird rekursiv wieder aufgerufen (Zeile 28). Die Rekursion terminiert, sobald rest nur noch ein Element besitzt, denn in diesem Fall wird in Zeile 13 das Funktions-Template für ein Argument printValueInfo(T value) verwendet.

Die Ausgabe zeigt die Typ- und Größeninformationen der Werte.

Wird das Programm mit den falschen oder keinen Argumenten aufgerufen, moniert dies der Compiler sofort mit einer Fehlermeldung.

Es gilt als Codierungsstandard in C++, beschrieben von Herb Sutter und Andrei Alexandrescu in ihrem Buch »C++ Coding Standards« (Sutter & Alexandrescu, 2005), Fehler zur Übersetzungszeit denen zur Laufzeit vorzuziehen. C++11 führt für die Zusicherung zur Übersetzungszeit das neue Schlüsselwort static_assert ein.

Aliase Templates

Dient static_assert dazu, den Template-Code robuster zu machen, so betreffen Aliase Templates vor allem die Lesbarkeit des Codes. Aliase Templates erlauben es, mittels des Schlüsselworts using Synonyme auf Templates zu erzeugen, die ihre Template-Parameter teilweise gebunden haben.

01 template< typename T, int V>
02 class MyType;
03
04 template< typename T>
05 using MyType10 = MyType<T,10>;
06
07 template<typename T>
08 using VecMyAlloc = std::vector<T,MyAllocator<T>>;

In MyType10 wird der zweite Template-Parameter von MyType mit 10 gebunden (Zeile 5). VecMyAlloc geht aus std::vector hervor, indem als zweites Element der Speicheranforderer (Zeile 8) gebunden wird.

Die Template-Instanziierung ist ein Prozess, der zur Übersetzungszeit stattfindet. Genauso verhält es sich mit den folgenden konstanten Ausdrücken.

Viele Datenkonzepte aus dem klassischen C++ wurden in C++11 aufgegriffen und erweitert. Diese Erweiterungen betreffen die konstanten Ausdrücke, die sogenannten Plain Old Data (POD), aber auch Enums. Neben den neuen String-Literalen R"raw string" und U"unicode string" kann der C++-Entwickler eigene Literale definieren.

Unbeschränkte Unions

Unions können in C++11 Elemente von Datentypen wie std::string mit nicht trivialen speziellen Elementfunktionen besitzen. Spezielle Elementfunktionen sind Funktionen, die der Compiler automatisch erzeugt.

Wird die Anwendung durch Unions erweitert, so wird sie durch Enums typsicherer.