Aus Linux-Magazin 04/2010

Der kommende C++-Standard C++0x

© Ablestock Premium, 123RF.com

An der Sprache C++ ändert sich selten etwas. Doch nun steht mit dem C++0x ein neuer Standard vor der Tür, von dem Neulinge wie Könner profitieren sollen. Mit Move-Semantik, Lambda-Funktionen und Aggregatzuweisungen bringt er viele Modernisierungen in die Programmiersprache .

Zwölf Jahre ist es schon her – sieht man vom ARM-C++-Standard im Jahr 1989 ab -, dass die ISO den Standard C++98 (ISO/IEC 14882) verabschiedet hat. Lediglich 2003 erfuhr er mit C++03 eine technische Korrektur. Umso gespannter darf der Programmierer auf den kommenden C++-Standard namens C++0x sein, denn dieser bringt mächtige Erweiterungen mit sich. Die Abkürzung bezieht sich auf den gescheiterten Plan, den Standard 2008/2009 zu verabschieden. Die Annahme bis zum Jahr 2011 gilt jedoch mittlerweile als sicher.

Renovierte Kernsprache

Während sich dieser Artikel vor allem den Neuerungen der Kernsprache widmet, wird ein Artikel in der nächsten Ausgabe die Neuheiten in der Bibliothek vorstellen.

C++0x bleibt folgenden Prinzipien der Programmiersprache C++ treu:

  • C++ ist eine Multi-Paradigmen-Programmiersprache [1].
  • Vertraue dem Programmierer.
  • Zahle nicht für etwas, das du nicht benutzt.
  • Brich keinen funktionierenden Code.
  • Kontrolle zur Compilezeit ist besser als zur Laufzeit.

Insbesondere soll der neue Standard eine bessere Programmiersprache für die Systemprogrammierung und die Implementierung von Bibliotheken werden. Auch an den Programmieranfänger denkt der C++-Schöpfer Bjarne Stroustrup, denn C++0x soll einfacher zu lehren und zu lernen sein. Obwohl sich C++0x laut Stroustrup wie eine neue Sprache anfühlt, bricht es nicht mit bestehendem C++-Code.

Rund um das Definieren von Klassen und deren Methoden enthält C++0x viele Verbesserungen. Sie helfen dem Novizen, seine Intention explizit auszudrücken, und dem Experten erlauben sie, das Verhalten seiner Datentypen zu optimieren. Listing 1 zeigt mehrere Klassendefinitionen, die jeweils mindestens ein neues Feature von C++0x demonstrieren.

Der Reihe nach: Instanzen der Klasse »NonCopyableClass« (Zeile 1) sind genau das, was sie versprechen, also nicht kopierbar. Nützlich ist diese Eigenschaft für die Arbeit mit Ressourcen wie Dateien, Locks oder Netzwerkverbindungen, für die sich kein sinnvolles Kopieren definieren lässt. Erreicht wird das Kopierverbot durch den Bezeichner »delete«. Während »delete« die durch den Compiler automatisch erzeugten Kopier- und Zuweisungsoperatoren in Zeile 2 und 3 unterbindet, bewirkt »default«, dass der durch den Compiler erzeugte Default-Konstruktor in Zeile 4 verwendet wird.

Mit diesen Bezeichnern lassen sich weitere bekannte C++-Idiome einfach umsetzen. So wird durch die Methode

void *operator new(std::size_t) = delete;

erzwungen, dass sich ein Objekt dieses Typs nicht mehr dynamisch mittels »new« erzeugen lässt.

Listing 1:
Klassendeklarationen

01 class NonCopyableClass{
02   NonCopyableClass & operator=(const NonCopyableClass&) = delete;
03   NonCopyableClass(const NonCopyableClass&) = delete;
04   NonCopyableClass() = default;
05 };
06 
07 class BaseClass{
08   virtual void foo();
09   void bar();
10 };
11 
12 class DerivedClass [[base_check]]: public BaseClass{
13   void foo [[override]] ();
14   void foo [[override]](int)
15   void bar [[hiding]] ();
16   void bar [[hiding]](int);
17 };
18 
19 class DelegateConstructor {
20 int value;
21 public:
22   DelegateConstructor(int v) : value(v) {}
23   DelegateConstructor() : DelegateConstructor(17) { }
24 };
25 
26 
27 class BaseClass{
28 public:
29   BaseClass(int i);
30 };
31 
32 class DerivedClass : public BaseClass{
33 public:
34   using BaseClass::BaseClass;
35 };
36 
37 template<typename T> class MyVector {
38 public:
39   MyVector(std::initializer_list<T> list);    // initializer-list constructor
40   MyVector(const MyVector&);                  // copy constructor
41   MyVector(MyVector&&);                       // move constructor
42   MyVector& operator=(const MyVector&);       // copy assignment
43   MyVector& operator=(MyVector&&);            // move assignment
44 };

Klare Verhältnisse

Es geht explizit weiter: Das Attribut »base_check« in der Klassendefinition in Zeile 12 sowie die beiden weiteren Attribute »override« und »hiding« unterbinden das zufällige Überladen oder Verstecken von Methoden der Basisklassen. Das Schlüsselwort »base_check« stellt sicher, dass jede Methode der abgeleiteten Klasse, die eine Methode der Basisklasse überlädt oder versteckt, dies ausdrücklich formuliert. Daher führt sowohl die Deklaration der Methode »foo(int)« aus Zeile 14 als auch die der Methode »void bar(int)« aus Zeile 16 zu einem Compilezeit-Fehler. Sollten in C++ alle Konstruktoren einer Klasse eine gemeinsame Initialisierungsroutine durchlaufen, war es bisher nötig, diese Routine in eine »init()«-Methode zu verpacken, sodass jeder Konstruktor sie aufrufen musste. Das ist fehleranfällig und geht nun einfacher, denn C++0x unterstützt die Delegation von Konstruktoren.

Beide Konstruktoren der Klasse »DelegateConstructor« rufen entweder direkt oder indirekt den Konstruktor »DelegateConstructor(int v)« auf, der dafür sorgt, dass »value« mit dem Wert 17 initialisiert wird. Konstruktoren lassen sich in C++0x nicht nur delegieren, sondern auch erben. Ein einfaches »using BaseClass::BaseClass« in Zeile 34 führt alle Konstruktoren der Basisklasse in die abgeleitete Klasse ein.

Die Containerklasse »MyVector« (Ähnlichkeiten mit dem C++-Datentyp »std::vector<int>« sind durchaus beabsichtigt), die über den Typ »T« parametrisiert ist, enthält zwei neue Konstruktoren. Zuallererst erlaubt der Initialiser-List-Konstruktor aus Zeile 39, dass sich »MyVector« über Sequenzen von homogenen Werten initialisieren lässt:

MyVector<int> myIntVec={1,2,3,4,5}

Die einfache Initialisierung von Standardcontainern über »{}«-Initialiser haben Programmierer schon lange vermisst. Jeder Standardcontainer besitzt diesen Initialiser-List-Konstruktor. Aber auch in eigenen Datentypen oder Funktionen lassen sich Initialiser-Listen verwenden: »double sumAll(std::initializer_list<double> list)«.

Move-Semantik

Die Standardcontainer erhalten außerdem zwei neue Methoden, die sich durch »&&« auszeichnen, so genannte Rvalue-Referenzen [4]. Diese Methoden, im Beispiel der Move-Konstruktor (Zeile 41) und das Move-Assignment (Zeile 43), statten den Datentyp mit Move-Semantik aus.

Ziel der Move-Semantik ist es, unnötiges Kopieren von Objekten zu vermeiden, denn die Move-Semantik transferiert beim Erzeugen von neuen Objekten die Inhalte aus bestehenden, während die Copy-Semantik diese kopiert.

Listing 2:
Move-Semantik

01 std::cout << "move semantic for vector: " << std::endl;
02 std::vector <int> vec1={1,2,3,4,5,6,7,8,9};
03 std::vector <int> vec2;
04 std::cout << "vec1.size(): " << vec1.size() <<  " vec2.size(): " << vec2.size() << std::endl;
05 vec2= std::move(vec1);
06 std::cout << "vec1.size(): " << vec1.size() <<  " vec2.size(): " << vec2.size() << "nn";
07 std::cout <<"copy semantic for vector: " << std::endl;
08 std::vector <int> vec3;
09 std::cout << "vec2.size(): " << vec2.size() <<  " vec3.size(): " << vec3.size() << std::endl;
10 vec3= vec2;
11 std::cout << "vec2.size(): " << vec2.size() <<  " vec3.size(): " << vec3.size() << std::endl;

Alles nur kopiert

Der Default-Konstruktor in Zeile 40 und der Default-Assignment-Operator in Zeile 42 setzen die Copy-Semantik um. Das Listing 2 soll die Unterschiede der Copy- und Move-Semantik verdeutlichen. Der Vektor »vec1« enthält zu Anfang die natürlichen Zahlen 1 bis 9, »vec2« hingegen ist leer. C++0x bildet den Aufruf der neuen Funktion »std::move« auf die Move-Assignment-Methode des Vektors ab, sodass dieser die Elemente von »vec1« übertragen bekommt (Abbildung 1). Im Gegensatz hierzu kopiert die Zuweisung »vec3= vec2« die Elemente des »vec2«, der seinerseits unverändert bleibt (Abbildung 2). Insbesondere beim Umgang mit großen Datenmengen bietet die Move-Semantik ein nicht zu unterschätzendes Optimimierungspotenzial, da sie jedes überflüssige Erzeugen und Kopieren von Daten vermeidet.

Erlaubt es die Move-Semantik, bei der Arbeit mit Containern an der Performance zu drehen, bringt C++0x zudem weitere Verbesserungen mit, die das Programmieren mit Containern der Standard Template Library (STL) deutlich vereinfachen. Das Initialisieren und Modifizieren der Containerelemente oder das Iterieren über diese wird damit wesentlich kompakter.

Abbildung 1: Die Move-Semantik in C++0x überträgt den Inhalt des einen Vektors in den anderen und vermeidet damit überflüssige Kopien.

Abbildung 1: Die Move-Semantik in C++0x überträgt den Inhalt des einen Vektors in den anderen und vermeidet damit überflüssige Kopien.

Abbildung 2: Die bisher übliche Copy-Semantik hinterlässt unnötige Kopien im Speicher, hier den Inhalt des ursprünglichen Vektors »vec2«.

Abbildung 2: Die bisher übliche Copy-Semantik hinterlässt unnötige Kopien im Speicher, hier den Inhalt des ursprünglichen Vektors »vec2«.

Abbildung 3: Die Ausgabe des Programms zeigt das Resultat der Lambda-Funktion und der Aggregatzuweisungen aus Listing 3.

Abbildung 3: Die Ausgabe des Programms zeigt das Resultat der Lambda-Funktion und der Aggregatzuweisungen aus Listing 3.

Einfacheres Arbeiten mit der Standard Library

Viele der neuen Features sind mit einem hochaktuellen GCC 4.5 schon lauffähig (siehe Kasten “Compilerunterstützung”). Zum Übersetzen von Listing 3 ist lediglich die Range-basierte For-Schleife »for (auto x : myInt)« in Zeile 8 auszukommentieren. Demgegenüber steht in Zeile 9 ein klassischer C++-Ausdruck, um über einen Container zu iterieren. Verpackt man den Codeschnipsel aus Listing 3 in ein Programm, kompiliert es und führt es aus, erscheint die erwartete Ausgabe (Abbildung 3). Die neuen »{}«-Initialiser räumen mit der uneinheitlichen Initialisierung von Objekten auf und lassen sich für alle Initialisierungen verwenden. Listing 3 zeigt, wie leicht das direkte Initialisieren eines Algorithmus (Zeile 25) oder eines Map-Containers (Zeile 27) durch ein Aggregat von der Hand geht.

Auch den Vektor in Listing 2 initialisiert ein Aggregat:

std::vector <int> vec1={1,2,3,4,5,6,7,8,9};

So weichen neun Methodenaufrufe einer Aggregatzuweisung. Gerade der C++-Novize wird dieses Feature dankbar annehmen, kann er doch jetzt intuitiv seine Objekte initialisieren. Ein paar weitere Beispiele (Listing 4) zeigen, dass »{}«-Initialiser natürlich auch im Kleinen anwendbar sind.

Compilerunterstützung

Im Stdcxx-Wiki [2] ist dargestellt, wie weit die bekannten Compiler den C++0x-Standard umsetzen. Diese Unterstützung bezieht sich allerdings nur auf die Kernsprache. Hier hat der GCC deutlich die Nase vorn. Dank der TR1-Bibliotheken des GCC 4.5 stehen darüber hinaus fast alle Bibliothekserweiterungen des neuen Standards zur Verfügung. Lediglich für Regular-Expression- und Threading-Bibliotheken sind Include-Dateien und Bibliotheken aus Boost [3] erforderlich.

Das ist durchaus legitim, dienen diese doch als Grundlage für die aktuelle Bibliothekserweiterung TR1. Mit ein bisschen Bastelarbeit ist es daher möglich, einen Großteil des neuen Standards C++0x im praktischen Einsatz kennenzulernen.

Listing 3: Rund und die Standard
Library

01 std::vector <int> myInt(10);
02 std::cout  << "vector of 10 times 0: ";
03 for (auto itr = myInt.begin(); itr != myInt.end(); ++itr){
04   std::cout << *itr << " ";
05 }
06 std::cout << "nn";
07 
08 // for (auto itr : myInt) { std::cout <<  *itr << " ";}
09 // for (std::vector<int>::const_iterator itr= intInt.begin(); itr != myInt.end(); ++itr){ ... }
10 
11 std::iota(myInt.begin(), myInt.end(),1);
12 std::cout << "Increment each value by 1: ";
13 for (auto itr = myInt.begin(); itr != myInt.end(); ++itr){
14   std::cout << *itr << " ";
15 }
16 std::cout << "nn";
17 
18 std::sort(myInt.begin(),myInt.end(),[](int a, int b){ return a > b;} );
19 std::cout <<  "Sort the vector of natural numbers: ";
20 for (auto itr = myInt.begin(); itr != myInt.end(); ++itr){
21   std::cout << *itr << " ";
22 }
23 std::cout  << "nn";
24 
25 std::cout<< "std::min({1,2,4,0,1,-5,2}): " << std::min({1,2,4,0,1,-5,2}) << "nn";
26 
27 std::map<std::string,std::string> phonebook =
28   {{"Bjarne","+1 (012) 555-1212"},
29    {"Herb", "+1 (345) 555-3434"},
30    {"Alexandrei","+1 (678) 555-5656"}};
31 std::cout << "Get each telephone number:  n";
32 for (auto itr = phonebook.begin(); itr!= phonebook.end(); ++itr){
33   std::cout << itr->first << ": " << itr->second << "n;
34 }

Listing 4:
Initialiser-Listen

01 Base x = Base{1,2};
02 Base* p = new Base{1,2};
03 
04 struct Derived : Base {
05   Derived(int x, int y) :Base{x,y} {};
06 };
07 
08 func({a});        // function invocation
09 return {a};       // function return value

Neue Algorithmen und Lambda-Funktionen

Ist der Container in Listing 3 initialisiert, kann ihn der Programmierer modifizieren. Dazu bietet C++0x neue Algorithmen an, »std::iota« in Zeile 11 etwa inkrementiert die Elemente des Vektors sukzessive um 1. Aber es gibt noch viele mehr [5]: »std::min« lässt sich entsprechend zum Algorithmus »std::sort« (Zeile 18) mit einer Funktion parametrisieren. Klassisches C++ würde dazu eine Funktion oder ein Funktionsobjekt [6] verwenden. Mit C++0x schreibt sich das viel kompakter, denn der neue Standard kennt Lambda-Funktionen, auch anonyme Funktionen genannt, sie sind eine Anleihe bei der funktionalen Programmierung. Damit lässt sich die Vergleichsfunktion genau an der Stelle definieren, an der sie benötigt wird.

Der folgende Ausdruck gibt 0 als Ergebnis zurück, denn es wird der minimale Wert über allen absoluten Werten gesucht:

std::min({1,2,4,0,1,-5,2},[](int a,int b){ U return abs(a) < abs(b); })

»,[](int a,int b){ return abs(a) < abs(b); }« ist die Lambda-Funktion. Die Struktur von Lambda-Funktionen in C++0x:

  • »[]«: Bindung an die Variablen des lokalen Scope.
    Dabei bedeutet »[]« keine Bindung, »[=]«
    die Werte werden kopiert, »[&]« referenziert die
    Werte des lokalen Scope.
  • »()«: Die Argumente des Funktionskörpers
    (optional).
  • »{}«: Der Funktionskörper.

Alle Details rund um Lambda-Funktionen in C++0x lassen sich bei Danny Kalev [7] ausführlich nachlesen.

Es wird noch mächtiger: Einerseits sind Lambda-Ausdrücke First Class Functions [8] und lassen sich insbesondere wie Daten behandeln. Andererseits ist mit »auto« (der letzte weiße Fleck aus Listing 3) automatisch der Typ ableitbar. Beide Komponenten ermöglichen – miteinander verknüpft – den einfachen Umgang mit Lambda-Funktionen.

Listing 5: »auto«
und Lambda-Funktionen verknüpft

01 auto myAdd= [](int a, int b){return a + b;};
02 std::cout << "myAdd(1,2): " << myAdd(1,2) << std::endl;

Listing 6:
»decltype«

01 std::vector <int > myVecInt={1};
02 std::vector <double> myDoubleInt={1.1};
03 decltype(myVecInt[0]*myDoubleInt[0]) myDouble = 2.1;

Automatik und Typ-Erkennung

Das Ausführen des fertigen Programms aus Listing 5 führt zur Ausgabe »myAdd(1,2): 3«. Neben »auto«, das den Typ aus einem Initializer ableitet, enthält C++0x noch das Schlüsselwort »decltype«, das den Typ aus einem Ausdruck ermittelt. Es ist ein mächtiges Werkzeug in generischen Kontexten, in denen der Ergebnistyp aus einer Operation nur schwer zu ermitteln ist. Der Ausdruck »decltype(myVecInt[0]*myDoubleInt[0]) myDouble« deklariert in Listing 6 »myDouble« vom Typ »double«.

Weiter geht es mit Neuerungen zum generischen Programmieren: Variadic Templates können mit einer beliebigen Zahl von Template-Argumenten umgehen. Ein praktisches Beispiel: Listing 7 verwendet Variadic Templates. Die Template-Funktion »f()« nimmt beliebig viele Argumente an und gibt deren Anzahl zurück. »printCommaSeparatedList()« erwartet ein oder mehrere Argumente und gibt diese kommasepariert aus.

Abbildung 4 zeigt die Ausgabe des Programms. Das Entscheidende sind die drei Punkte, die entweder links in »template <typename… Args>« oder in »int f(Args… args){« rechts vom Parameter »Args« stehen. Links packt der Ellipsen-Operator »…« das so genannte Parameter Pack, rechts entpackt er es wieder. Eine Besonderheit ist der neue Operator »sizeof …« (Zeile 7), der direkt mit Parameter Packs umgehen kann.

Abbildung 4: Variadic Templates können mit einer beliebigen Zahl von Argumenten umgehen – die Ausgabe von Listing 7.

Abbildung 4: Variadic Templates können mit einer beliebigen Zahl von Argumenten umgehen – die Ausgabe von Listing 7.

Listing 7: Variadic
Templates

01 #include <iostream>
02 #include <string>
03 #include <vector>
04 
05 template <typename... Args>
06 int f(Args... args){
07   return (sizeof... args);
08 }
09 
10 template<typename T>
11 void printCommaSeparatedList(T value)
12 {
13   std::cout<<value<<std::endl;
14 }
15 
16 template<typename First,typename ... Rest>
17 void printCommaSeparatedList(First first,Rest ... rest)
18 {
19   std::cout<<first<<",";
20   printCommaSeparatedList(rest...);
21 }
22 
23 
24 int main() {
25   std::cout << "n";
26 
27   auto intList= {1,2,3};
28   std::vector <int> intVec= {1,2,3,4,5};
29 
30   std::cout << "f() has " << f() << " argumentsn";
31   std::cout << "f(42, 3.14) has " << f(42, 3.14) << " argumentsn";
32   std::cout << "f("one","two","three","four") has " << f("one","two","three","four" ) << " argumentsn";
33   std::cout << "f(intVec,intList) has " << f(intVec,intList) << " argumentsnn";
34 
35   printCommaSeparatedList("Only primary template used.");
36   printCommaSeparatedList(42,"hello",2.3,'a');
37   std::cout << "n";
38 }

Listing 8:
Compilezeit-Konstanten

01 constexpr int square(int x) { return x * x; }
02 int values[square(7)];

Rekursion

Deutlich ungewohnter zeigt sich die Template-Funktion »printCommaSeparatedList()«. Sie besteht aus dem Template in Zeile 10, das nur ein Argument erwartet und dem gleichnamigen Template in Zeile 16, das mehr als ein Argument erwartet. Beim Aufruf mit einem einzigen Argument, beispielsweise mit

printCommaSeparatedList("Only primary U template used.");

kommt das Template mit einem Argument zum Einsatz und gibt den String aus. Dagegen stößt der Aufruf

printCommaSeparatedList(42,"hello",2.3,'a');

die Rekursion an, gibt das erste Argument mit der Anweisung »std::cout <<first<<“,”;« aus und ruft sich rekursiv mit der Anweisung »printCommaSeparatedList(rest…);« auf. Dieser Prozess kommt genau dann zum Erliegen, wenn »rest« nur noch ein Element enthält, denn jetzt wird das Template mit einem Argument verwendet.

Pattern Matching und rekursive Aufrufe sind elementare Bausteine der funktionalen Programmierung [8]. Ein interessanteres Beispiel für Variadic Templates ist die Funktion »printf()« [9]: Im Gegensatz zu ihrem C-Pendant ist sie typsicher.

Die Technik Template Metaprogramming [10] zeichnet sich dadurch aus, dass der Code zur Compilezeit ausgeführt wird und somit zur Laufzeit bereits als Konstante zur Verfügung steht.

Damit das funktioniert, müssen sich die Ausdrücke zur Compilezeit evaluieren lassen. Genau dies bewirkt das neue Schlüsselwort »constexpr« in Listing 8. Damit lassen sich einfache Funktionen, die einen Return-Wert besitzen, sowie auch Objekte auswerten. Das Array in Listing 7 beispielsweise lässt sich in C++0x über die Compilezeit-Konstante initialisieren. Die genaueren Forderungen an »constexpr« sind im Wikipedia-Eintrag unter [11] nachzulesen.

Das Schlüsselwort »static_assert« macht es möglich, konstante Ausdrücke ohne die Magie des Template Metaprogramming zur Compilezeit zu evaluieren. Dies ist besonders hilfreich, weil der Programmierer mit »static_assert« Voraussetzungen für die Templateparameter prüfen kann. Der Ausdruck

static_assert(sizeof(int) == 4, "This code U only works for sizeof(int) == 4");

stellt sicher, dass »int« die richtige Größe besitzt, und führt im Fehlerfall dazu, dass das Kompilieren abbricht und der String »”This code only works for sizeof(int) == 4″)« ausgegeben wird.

Und noch viel mehr

Der kommende Standard C++0x bringt noch viele weitere interessante Features mit: streng typisierte Enums, verallgemeinerte Plain Old Data (POD) und Unions. Er führt das Literal »nullptr« ein, aber auch neue String-Literale wie »R”raw string”«. Zudem interpretiert C++0x die zwei abschließenden Klammern in dem Ausdruck »std::list<std::vector <int>>« nicht mehr irrtümlich als Rechts-Shift-Operator. C++0x integriert zu großen Teilen den aktuellen C-Standard C99. Somit steht der Datentyp »long long« bereit. Concepts – ein Typchecking für Templates, zunächst als ein Hauptfeature in C++0x angesehen – flog im Juli 2009 allerdings aus dem aktuellen Standard. Die Neuerungen in C++0x sind vielfältig. Danny Kalev, ehemaliges Mitglied der C++-Standard-Komitees, sollte aber in seiner umfassenden Online-Artikelreihe rund um C++0x [12] kaum noch eine Frage offen lassen.

Ein zweiter Artikel in der nächsten Ausgabe des Linux-Magazins verlässt die Kernsprache und widmet sich den neuen Bibliotheken. Es gibt viel vorzustellen, denn die meisten Erweiterungen des C++-Standards finden in der Standardbibliothek statt. (mhu)

Infos

[1] Multiparadigmen-Programmiersprache: [http://de.wikipedia.org/wiki/C%2B%2B]

[2] Compiler-Unterstützung für C++0x: [http://wiki.apache.org/stdcxx/C++0xCompilerSupport]

[3] Boost-Bibliotheken: [http://www.boost.org]

[4] Rvalue-Referenzen: [http://www2.research.att.com/~bs/C++0xFAQ.html#rval]

[5] Neue Algorithmen: [http://www2.research.att.com/~bs/C++0xFAQ.html#algorithms]

[6] Funktionsobjekte: [http://en.wikipedia.org/wiki/Function_object]

[7] Lambda-Funktionen in C++0x: [http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=254]

[8] Rainer Grimm, “Funktionale Programmierung (1): Grundzüge”: [https://www.linux-magazin.de/Online-Artikel/Funktionale-Programmierung-1-Grundzuege]

[9] »printf()« mit Variadic Templates: [http://en.wikipedia.org/wiki/C%2B%2B0x#Variadic_templates]

[10] Template Metaprogramming: [http://de.wikipedia.org/wiki/C%2B%2B-Metaprogrammierung]

[11] Constant Expressions: [http://en.wikipedia.org/wiki/C%2B%2B0x#Generalized_constant_expressions]

[12] Danny Kalev, “C++0x: The New Face of Standard C++”: [http://www.informit.com/guides/content.aspx?g=cplusplus&seqNum=216]

LINUX-MAGAZIN KAUFEN
EINZELNE AUSGABE Print-Ausgaben Digitale Ausgaben
ABONNEMENTS Print-Abos Digitales Abo
TABLET & SMARTPHONE APPS Readly Logo
E-Mail Benachrichtigung
Benachrichtige mich zu:
0 Kommentare
Älteste
Neuste Beste Bewertung
Inline Feedbacks
Alle Kommentare anzeigen
Nach oben