[X] C++09 (Teil 1) - Ein Überblick: Neue Sprachfeatures



  • C++09 (Teil 1) - Ein Überblick: Sprachfeatures

    C++ ist seit der Veröffentlichung des internationalen Standards 1998 schon etwas in die Jahre gekommen. Zwar gab es 2003 eine Überarbeitung, die einige Makel behoben hat, jedoch an den Grundstrukturen der Sprache nicht wirklich etwas verändert hat.

    Der nächste, ziemlich große Sprung, wird die Veröffentlichung eines neuen internationalen C++-Standards, vermutlich 2009, sein. C++09 ist eine Überarbeitung, die in vielen Bereichen der Sprache grundlegende Änderungen vornimmt. Auch die Standardbibliothek wird um einiges vielfältiger. Oft liest man auch die Bezeichnung C++0x. Ich habe hier bewusst C++09 genommen, um den Standard auch vom Namen her "greifbarer" zu machen.

    Da wir mittlerweile Cschon 2008 schreiben, liegt die Veröffentlichung des neuen Standards in nicht allzu weiter Ferne und von vielen Features lässt sich schon fix sagen, dass sie dabei sein werden. Besser noch: So manch aktueller Compiler (GCC 4.3, Comeau 4.3.9) implementiert bereits einige Neuerungen.

    Dieser Artikel ist Beginn einer Reihe, die sich mit den wichtigsten und sichtbarsten Neuerungen befasst. Hier soll zunächst ein grober Überblick über die neuen Sprachfeatures gegeben werden. Da C++09 sehr viel Neues enthält, ist klar, dass nicht auf alles im Detail eingegangen werden kann. Ich beschränke mich hier auf die "wichtigsten" und "sichtbarsten" Features, das sind jene Möglichkeiten, die am meisten ins Auge stechen und vermutlich den größten Einfluss auf den zukünftigen C++-Programmierstil haben werden. Da es jedoch immer noch eine persönliche Auswahl darstellt, ist klar, dass nicht jede Neuerung besprochen werden kann.

    Da der neue C++-Standard noch nicht fertig ist, kann es natürlich sein, dass die eine oder andere Neurung im tatsächlichen Standard dann nicht oder nicht in der hier vorgestellten Form inkludiert sein wird. Der aktuelle Status der Features, die hier vorstellt werden, wird deshalb mitangegeben.

    Inhalt

    • 1. Neue Konzepte
    • 1.1. Variadic Templates
    • 1.2. Rvalue-Referenzen: Move statt Copy
    • 1.3. concept und concept_map
    • 1.4. static_assert
    • 1.5. Über die neue Art, Funktionen zu deklarieren
    • 1.6. Lambda-Ausdrücke
    • 1.7. Neue Literale
    • 2. Bewährtes verbessert
    • 2.1. Templates neu
    • 2.2. Konstruktion von Objekten
    • 2.3. Konstante Ausdrücke
    • 2.4. for für Ranges
    • 2.5. Explizite Umwandlungsoperatoren
    • 2.6. Typensicheres enum
    • 2.7. default -Funktionen
    • 2.8. Plötzlich POD
    • 2.9. nullptr
    • 3. Ausblick
    • 4. Quellen und Verweise

    1 Neue Konzepte

    Die Features, die in diesem Teil aufgelistet sind, sind größere, zusammenhängende Neuerungen, die die Möglichkeit, Sachverhalte mit C++ auszudrücken enorm verändern. Diese Änderungen stechen auch AnfängerInnen sofort ins Auge.

    1.1 Variadic Templates

    Oft ist es nötig, einem Template zu erlauben, eine beliebige Zahl von Argumenten entgegenzunehmen. Variadic Templates erlauben genau das.
    Mehr noch: Mit ihnen kann man auch Funktionen definieren, die eine beliebige Anzahl von Argumenten annehmen können und dennoch typsicher sind. Damit erlauben Variadic Templates die einfachere, sauberere und wartbarere Implementierung beispielsweise von Tupel-Typen oder Funktionsobjekten und typsicheren Funktionen mit einer beliebigen Anzahl von Parametern wie std::make_shared (siehe C++09 Teil 2 - ein Überblick: Die Standardbibliothek).

    In Zusammenhang mit Rvalue-Referenzen ermöglichen Variadic Templates sogenanntes perfect forwarding, die perfekte Weitergabe von Argumenten durch verschiedene Funktionen. Ein Beispiel dafür findet sich im Kapitel über Rvalue-Referenzen. (1.2)

    Beispiel 1 - Variadic Klassentemplates

    //count_args zählt, mit wievielen Template-Parametern es instanziiert wird
    template <typename ...Args>
    struct count_args;
    
    template <>
    struct count_args<>
    {
       static int const value = 0;
    };
    
    template <typename T, typename ...Args>
    struct count_args<T, Args...>
    {
       static int const value = 1 + count_args<Args...>::value;
    };
    
    //Anwendung
    assert (count_args<int, int, int>::value == 3);
    assert (count_args<int, double, float, char>::value == 4);
    

    Erläuterung
    Das Template count_args ist, wie in der Template-Metaprogrammierung üblich, rekursiv über explizite Spezialisierung definiert.

    Mit der Syntax template <typename ...Args> wird ein Template deklariert, dass eine beliebige Anzahl (0-…) von Argumenten entgegennimmt. ...Args nennt man ein Parameter-Pack. Mithilfe des ... (Ellipsen oder Auslassungspunkt)-Operators wird dieses Pack "ausgepackt", aus count_args<Args...>::value wird hier also count_args<Arg1, Arg2, Arg3, ...>::value .

    Beispiel 2 - Variadic Templates in Funktionen

    template <class T, typename ...Args>
    T * make_new (Args&& ...args) //Das && deklariert eine Rvalue-Referenz. Siehe 1.2
    {
       return new T(static_cast<Args&&>(args)...); //Entpackt wird so:
       //new T(static_cast<Arg1&&>(arg1), static_cast<Arg2&&>(arg2), ...)
    }
    

    Erläuterung
    ...Args erzeugt wie gehabt ein Parameter-Pack, doch diesmal genauer: ein Template-Parameter-Pack. Die Deklaration Args const& ...args erzeugt zwar auch eine Parameter-Pack, aber in diesem Fall ein Funktions-Parameter-Pack. Funktions-Parameter-Packs auspacken funktioniert genauso wie mit Template-Packs: args... .

    Generell lässt sich sagen, dass immer dann ein Parameter-Pack erzeugt wird, wenn der Ellipsis-Operator links von einem Namen steht und einen Parameter-Pack entpackt, wenn er rechts von ihm steht. Dabei spielt keine Rolle, wie kompliziert der Ausdruck ist, in dem ein Parameter-Pack erweitert (ausgepackt) wird.

    template <class T, class ...Args>
    T* make_new (Args&& ...args)
    {
       //expandiert automatisch richtig:
       return new T( *new Args(static_cast<Args&&>(args)) ...); 
    }
    

    Status
    Variadic Templates sind bereits im derzeitigen Working-Draft des kommenden Standards enthalten.

    Unterstützung
    Die Implementation für Compiler-Hersteller scheint nicht allzu schwer zu sein. GCC 4.3 unterstützt Variadic Templates, die Standardbibliothek libstdc++ 4.3. dieses Compilers stützt sich bereits darauf.

    Proposals
    N2080 - Hintergründe
    N2087 - Eine kurze Einführung

    1.2 Rvalue-Referenzen: Move statt Copy

    Das Problem:

    string operator + (string const& a, string const& b)
    {
       string tmp (a);
       a += b;
       return tmp;
    }
    
    string a ("this ");
    string b = a + "a" + "b" + "cd";
    

    In dem Ausdruck b = a + … werden für jeden Aufruf von operator+ temporäre Objekte erzeugt, die anschließend in die lokale Variable tmp kopiert werden. Das ist nötig, da temporäre Objekte nur an konstante Referenzen gebunden werden können (und das aus gutem Grund). Damit wird für jeden Aufruf von operator+ (typischerweise) dynamischer Speicher allokiert. In obigen Ausdruck würde dies zumindest dreimal geschehen.

    Mit Rvalue-Referenzen ist es möglich, statt ständig zu kopieren, den Inhalt der temporären Variablen zu verschieben - das ist sicher, denn temporäre Variablen existieren - wie ihr Name schon sagt - nur temporär, in unserem Fall gibt es sie nach Auswertung des Ausdrucks b = a + … nicht mehr.

    Manche Objekte in C++ sind nicht kopierbar, können aber sehr wohl verschoben ("move") werden. Ein Beispiel dafür sind fstream-Objekte. Wenn diese Move-Semantik unterstützen, können sie auch jederzeit sicher in Standard-Containern aufbewahrt werden, da immer nur ein Objekt (bzw. dessen interne Repräsentation) und keine Kopie existiert.

    In Kombination mit Variadic Templates ist perfect forwarding möglich, ebenso die Implementation einer swap -Funktion, die ohne temporäre Kopie (und damit einer möglichen Allokation von dynamischem Speicher) arbeitet.

    Beispiel - Optimierung von std::string

    string&&
    operator + (string&& a, string const& b)
    {
       return a += b;
    }
    
    string&&
    operator + (string const& a, string&& b)
    {
       return b.insert(0, a);
    }
    
    string&&
    operator+(string&& a, string&& b)
    {
       return a += b;
    }
    

    Erläuterung
    Die Syntax T && t = T() bindet die Rvalue-Referenz t an ein temporäres Objekt T() .
    Die oben definierten Überladungen von operator+ können daher unterscheiden, ob sie temporäre Objekte (genauer: Rvalues) übergeben bekommen. Falls ein temporäres Objekt übergeben wurde, kann direkt mit diesem gearbeitet werden, was eine Allokation von neuem Speicher (meist) überflüssig macht. Sollte die NRVO (Named-Return-Value-Optimization) nicht funktionieren, wird auch das zurückgegebene Objekt nicht kopiert, sondern nur dessen Inhalt "verschoben", ein passender Move-Constructor, der Rvalue-Referenzen annimmt, vorausgesetzt.

    Beispiel - Forwarding

    #include <utility>
    #include <memory>
    
    struct A
    {
       A (double&, float&, const int&);
    };
    
    template <class T, class ...Args>
    shared_ptr<T>
    factory_method (Args&&... args)
    {
       return std::shared_ptr<T>(new T(std::forward(args)...));
    }
    
    //Anmerkung: Diese Funktion ist in <memory> als make_shared definiert.
    

    Erläuterung
    std::forward ist in <utility> definiert und gibt schlicht und einfach ihr Argument weiter. Wenn es eine Rvalue-Referenz übergeben bekommt, gibt es auch eine Rvalue-Referenz weiter. Ansonsten müsste man explizit casten.

    Status
    Rvalue-Referenzen sind bereits im derzeitigen Working-Draft des kommenden Standards enthalten.

    Unterstützung
    Sowohl Rvalue-Referenzen als auch deren effektiver Einsatz in der Standardbibliothek werden von einigen Compilern unterstützt. Für obiges Beispiel string a = b + … braucht Metrowerks CodeWarrior Compiler nur einen einzige dynamische Speicherallokation. GCC 4.3 unterstützt Rvalue-Referenzen ebenso.

    Proposals
    N1690 - Anwendungsfälle für Rvalue-Referenzen
    N2027 - Kurze Einführung

    1.3 concept und concept_map

    Concepts vereinfachen die Verwendung von Templates, indem sie sicherstellen, dass übergebene Template-Parameter sich an definierte Vorgaben halten. Seitenlange Fehlermeldungen, wie sie heute beim Einsatz von Templates oft vorkommen, können durch Concepts. kürzer und damit aussagekräftiger gemacht werden. Im Grunde stellen Concepts ein Typsystem für Templates dar.

    Beispiel 1 - Grundlagen

    auto concept LessThanComparable <typename T>
    {
       bool operator<(T, T);
    };
    
    template <LessThanComparable T>
    T const& min (T const& a, T const& b)
    {
       return a < b ? a : b;
    }
    

    Erläuterung
    Das Concept LessThanComparable beschreibt alle Typen T, die einen operator< implementieren, der zwei Ts vergleichen kann und einen boolschen Wert zurückgibt.
    Das Template min akzeptiert damit nur jene Typen, die dieses Concept unterstützen.
    Sollten sie dies nicht tun, kann der Compiler eine aussagekräftige Fehlermeldung wie "template-Parameter T unterstützt concept LessThanComparable nicht" generieren.
    Das auto bedeutet, dass jeder Typ, der die Bedingungen, die das Concept formuliert, unterstützt, automatisch auch das Concept unterstützt.

    Beispiel 2 - Multityp-Bedingungen

    auto concept Convertible <typename T, typename U>
    {
       operator U (T const&);
    };
    
    template <typename U, typename T>
      requires Convertible<T, U>
    U convert (T const& t)
    {
       return t;
    }
    

    Erläuterung
    Das Concept Convertible bezieht sich auf zwei Typen, die eine Konvertierungsbeziehung zueinander besitzen. Um dem Template convert zu sagen, dass es nur T-zu-U konvertierbare Typen akzeptieren soll, ist eine require -Bedingung notwendig.

    Beispiel 3 - Concept-Maps

    concept InputIterator <typename Iter> //Ein Input-Iterator hat…
    {
       typename value_type;               //einen Typ "value_type"
       typename reference;                //einen Typ "reference"
       typename pointer;                  //einen Typ "pointer"
       typename difference_type;          //einen Typ "difference_type"
                                          //und kann
       reference operator* (Iter const&); //dereferenziert werden
       Iter& operator ++ (Iter&);         //inkrementiert werden (prä)
       Iter& operator ++ (Iter&, int);    //inkrementiert werden (post)
    };
    
    concept_map InputIterator<char*>
    {
       typedef char value_type;
       typedef char& reference;
       typedef char* pointer;
       typedef ptrdiff_t difference_type;
    };
    

    Erläuterung
    Concepts beschreiben eine gewisse Schnittstelle, mit der generische Algorithmen arbeiten können. So beschreibt das Concept InputIterator die von Input-Iteratoren geforderte Schnittstelle. Ein Zeiger (in diesem Fall auf char) besitzt jedoch nicht das verlangte Interface, vor allem nicht die Typen char*::value_type etc. Interessant zu erwähnen ist außerdem, dass jeder Operator, der von einem Concept benötigt wird, innerhalb des Concepts als freistehende Definition deklariert werden muss.

    Mit einer Concept-Map kann man nun für einen beliebigen Typen beschreiben, wie dessen Interface gegenüber einem gewissen Concept aussieht. Man sagt damit also, dass ein gewisser Typ ein Concept unterstützt - und wie er es unterstützt. Damit ist es möglich, Interfaces von selbst-implementierten Typen derart umzubiegen, so dass sie bestimmte Konzepte unterstützen.

    Beispiel 4 - Schnittstellen verbiegen
    Man stelle sich eine eigene Implementation eines Strings vor, sowie ein Konzept, das Strings beschreibt:

    concept String <typename S>
    {
        //…
        S concatenate (S const&, S const&);
    };
    
    class MyString
    {
         //…
         MyString mycat (MyString const& b);
    };
    

    Mit einer Concept-Map ist es nun möglich zu sagen, dass MyString das Concept String unterstützt, ohne etwas an der eigentlichen Schnittstelle von MyString zu verändern.

    concept_map String<MyString>
    {
       //…
       MyString concatenate (MyString const& a, MyString const& b)
       {
          S tmp(a);
          return tmp.mycat(b);
       }
    };
    
    concept_map String<std::string>
    {
       //…
       std::string concatenate (std::string const& a, std::string const& b) 
       {
          std::string tmp(a);
          return tmp += b;
       }
    };
    

    Damit lassen sich MyString und std::string für alle Algorithmen verwenden, die das String-Concept unterstützen, obwohl MyString und std::string zwei gänzlich unterschiedliche Schnittstellen anbieten.

    Status
    Concept und Concept-Map sind noch nicht im Working-Draft, jedoch in der letzten Revisionsphase. Es ist wohl sicher, davon auszugehen, dass sie kommenden Standard vorhanden sind. Geklärt werden müssen nur mehr Details, was das Wording angeht.

    Unterstützung
    Keiner der größeren Compiler unterstützt dieses Feature. Es gibt allerdings eine angepasste Version der GCC, die concept und concept_map unterstützt: ConceptGCC.

    Proposals
    N2081 - Programmieren mit Concepts
    N2398 - Voraussichtliche Formulierung

    1.4 static_assert

    Zur Überprüfung von Assertions stellte C++ bisher zwei Mechanismen zur Verfügung: Das Makro assert aus <cassert> und die #error -Direktive des Präprozessors.

    Das Makro assert stellt dabei die Möglichkeit zur Verfügung, bestimmte Behauptungen zur Laufzeit zu überprüfen (z.B. Invarianten), die #error -Direktive hat schon vor der Kompilierung Auswirkungen.

    Für die Template-Metaprogrammierung wird jedoch häufig noch eine zusätzliche Art von Assertion gebraucht, eine Art von Assertion, die zur Compile-Zeit selbst überprüft wird. Damit kann man unter anderem die Instanziierung eines Templates mit einem nicht adäquaten Typen verhindern und bekommt eine halbwegs lesbare Fehlermeldung. Solche sogenannten Static-Assertions sind derart verbreitet, dass boost beispielsweise ein eigenes Makro dafür anbietet: BOOST_STATIC_ASSERT . Die Fehlermeldungen, die davon generiert werden, sind jedoch oft nicht aussagekräftig genug, zumindest nicht so aussagekräftig, wie sie sein könnten.

    Um statische Assertions einfacher zu machen, wurde das neue Schlüsselwort static_assert eingeführt.

    Beispiel

    #include <limits>
    #include <type_traits>
    
    static_assert (std::numeric_limits<int>::digits >= 32, "fehler: int zu klein")
    
    template <class Base, class Derived>
    struct Foo
    {
        static_assert (std::is_base_of<Base, Derived>::value, "fehler: Derived nicht von Base abgeleitet");
    };
    

    Erläuterung
    static_assert erwartet zwei Parameter: Einen konstanten Ausdruck und ein String-Literal.
    Die Verwendung erklärt sich im Prinzip von selbst: Liefert der konstante Ausdruck true , hat die static_assert -Deklaration keinen Effekt. Andernfalls wird das Kompilieren abgebrochen. In diesem Fall ist der Compiler zwar nicht dazu verpflichtet, die Fehlermeldung, die im Stringliteral steht, auszugeben, wird jedoch dazu angehalten dies zu tun. Eine static_assert -Deklaration kann überall dort vorkommen, wo es auch eine using -Deklaration darf.

    Status
    Die static_assert -Deklaration ist bereits im derzeitigen Working-Draft des kommenden Standards enthalten.

    Unterstützung
    static_assert wird von GCC 4.3, Comeau 4.3.9 unterstützt.

    Proposals
    N1720 - Hintergrund, Verwendung, Formulierung

    1.5 Über die neue Art, Funktionen zu deklarieren

    Will man den Typ einer Variable in C++03 festlegen, so muss man dies explizit tun. Die einzige Möglichkeit, dies abzukürzen, stellen typedefs dar. Dies verwendet man beispielsweise in for -Schleifen wie der folgenden:

    typedef typename vector<T>::iterator iter;
    for (iter i = v.begin(); …
    

    In C++09 übernimmt das Schlüsselwort auto die automatische Bestimmung des Types einer Variablen durch den Ausdruck, der sie initialisiert.

    auto x1 = 3.1415; //x ist double
    
    int foo ();
    auto x2 = foo();
    auto const& x3 = foo(); //auto kann mit cv-Qualifizierern, * und & verwendet werden
    

    Eine weitere Möglichkeit, den Typ eines bestimmten Ausdrucks zu bestimmen, bietet das neu eingeführte Schlüsselwort decltype an.

    int i;
    double d;
    int foo ();
    
    decltype(i + i); //int
    decltype(i * d); //double
    decltype(foo()); //int
    decltype(foo); //int()()
    

    Eine der möglichen Verwendungen für decltype , die man bei dessen Entwicklung im Hinterkopf hatte, war die automatische Bestimmung des Rückgabetyps einer Funktion. Zunächst ein Beispiel, wie man den eine Rückgabe-per-Parameter (als Referenz) mit decltype realisiert.

    template <class T, class U>
    void add (T const& a, U const& b, decltype(a + b) & result);
    

    Auch der Typ des Rückgabewerts einer Funktion sollte durch decltype automatisch hergeleitet werden. Allerdings stößt man hier auf ein Problem. Die angenehme Syntax, die eben noch möglich war, versagt, da die Parameter a und b noch nicht deklariert wurden:

    template <class T, class U>
    decltype(a + b) //Fehler: Woher kommen a und b?
    add (T const& a, U const& b); //Hier erst werden sie deklariert!
    

    Die Lösung liegt in der zweiten großen Verwendungsmöglichkeit des Schlüsselwortes auto .
    Das alles kombiniert erfordert allerdings die Verwendung einer neuen Art von Funktionsdeklaration.

    Beispiel - Funktionsdeklaration in C++09

    template <class T, class U>
    auto operator+ (T const& a, U const& b) -> decltype(a + b);
    
    //Dies ist _nicht_ auf Templates beschränkt.
    auto foo () -> int;
    
    class Bar
    {
       auto quux () const -> int throw ();
    };
    
    //Damit werden typedefs und Funktionszeiger auch einfacher handhabbar:
    typedef (auto (int) -> int) F;
    F foo; //äquivalent zu int foo (int);
    
    typedef (auto (double) -> double) * PF; //Zeiger auf Funktion
    
    (auto (int) -> int)* a[10]; //Array von Funktionszeigern
    
    //Zeiger auf Funktion, die einen Funktionszeiger zurückgibt:
    typedef (auto (int) -> (auto (double) -> double) *) *PFF;
    

    Erläuterung
    Ursprünglich für den Einsatz mit decltype bei der automatischen Bestimmung des Rückgabetyps einer Funktion gedacht, bringt diese neue Art, Funktionen zu deklarieren Vorteile vor allem in der Lesbarkeit.

    Status
    auto und decltype sind bereits im derzeitigen Working-Draft des kommenden Standards enthalten. Die neue Art von Funktionsdeklaration befindet sich in der finalen Planungsphase.

    Unterstützung
    decltype wird von GCC 4.3. unterstützt. auto mit der neuen Funktion wird durch den Comeau-Compiler unterstützt. Die Funktionsdeklaration mit der neuen Syntax wird meines Wissens nach noch von keinem Compiler angeboten.

    Proposals
    N1979 - Erläuterungen zu decltype und der neuen Funktionsdeklaration
    N1984 - automatische Typherleitung ( auto )

    1.6. Lambda-Ausdrücke

    Viele Programmiersprachen, vor allem viele funktionale Programmiersprachen, unterstützen es, lokale, namenlose Funktionen on-the-fly innerhalb einer Funktion zu definieren. Diese Funktionen haben verschiedene Einsatzmöglichkeiten: Beispielsweise könnten Funktionen höherer Ordnung wie for_each unter C++ mit ihnen arbeiten. Im Moment wäre for_each noch auf einen Funktor oder eine Funktion angewiesen, die extra angelegt und benannt werden müsste. Solche Funktionen, die on-the-fly erstellt werden können, nennt man Lambda-Funktionen, manchmal auch Closures.

    Mit C++09 könnte sich das ändern. Ein Lambda-Ausdruck (bzw. eine Lambda-Funktion) in C++09 spezifiziert ein anonymes Funktions-Objekt. "Closure" ist die Bezeichnung für das danach erstellte Funktionsobjekt. Ein Closure beinhaltet neben der eigentlichen Funktion noch Variablen aus dem Sichtbarkeitsbereich, in dem die Lambda-Funktion vorkommt.

    Lambda-Ausdrücke bauen auf der neuen Art, Funktionen zu deklarieren (Siehe 1.5) auf.

    Beispiel

    //Eins: Lambda-Ausdruck als "Single-Expression"
    int i = <>(int x, int y) (x + y) (42, 23); 
    
    //Zwei: In der "Funktionsblock"-Version:
    int j = <>(int x, int y) -> int { int z; z = x + y; return z; } (1, 2); 
    
    //Drei:
    int array [] = { 1, 2, 3, 4 };
    int sum = 0;
    
    //Bildet die Funktionalität von std::accumulate nach, expliziter Zugriff auf sum:
    for_each (array, array+4, <>(int element : &sum) (sum += element));
    
    //Vier: Zugriff auf alle Variablen aus dem Scope des Lambda-Ausdrucks
    for_each (array, array+4, <&>(int element) (sum += element));
    

    Erläuterung
    Lambda-Ausdrücke sollen vom Compiler in normale Funktionsobjekte übersetzt werden. Aus dem ersten Lambda-Ausdruck (Eins) in unserem Beispiel soll vom Compiler ein Code erzeugt werden, der so oder so ähnlich aussieht:

    struct lambda_1
    {
       int operator () (int x, int y) { return x + y; }
    }
    int i = lambda_1()(42,23);
    

    Die Möglichkeit, der Lambda-Funktion Variablen mitzugeben, (Beispiel Drei), wird durch die spezielle Syntax <>(int argument : &referenz_auf_variable1_von_aussen, variable2_von_aussen) ermöglicht. Man kann sich dies so vorstellen, dass der Compiler dem Konstruktor des Funktionsobjekts Referenzen bzw. Kopien lokaler Variablen mitgibt.

    Status
    Es ist nicht ganz sicher, ob Lambda-Ausdrücke im kommenden Standard aufgenommen werden. Das Design und die Entwicklung von Lambda-Ausdrücken scheint komplett, allerdings hat die Kern-Arbeitsgruppe sich des Themas noch nicht angenommen. Dies kann darin liegen, dass der Wortlaut für die Änderung im Standard noch nicht klar war.

    Unterstützung
    Mir sind keine C++-Compiler bekannt, die Lambda-Funktionen unterstützen.

    Proposals
    N2413 - Erläuterungen und Beispiele

    1.7. Neue Literale

    Die grundlegende Einheit im Speichermodell von C++ ist das Byte.

    ANSI ISO IEC 14882:2003 schrieb:

    A byte is at least large enough to contain any member of the basic execution character set and is composed of a contiguous sequence of bits, the number of which is implementation-defined.

    Der neue Standard erweitert diese Definition und schreibt vor, dass ein Byte zumindest auch jene 8-Bit Einheiten von UTF-8 unterstützen soll.

    String-Literale sind Zeichensequenzen, die von hochgestellten doppelten Anführungszeichen umschlossen sind, also beispielsweise "Quux" und "Fubar" . C++03 stellt zwei Arten von String-Literalen zur Verfügung: Normale String-Literale und Wide-String-Literale. Erstere sind vom Typ Array von char , letztere vom Typ Array von wchar_t . Wide-String-Literale werden durch ein "L" als Präfix gekennzeichnet.

    "Quux"; //String-Literal
    L"Bar"; //Wide-String-Literal
    

    C++09 führt einige neue Arten von String-Literalen ein.

    • UTF-8 String-Literale. Diese beginnen mit dem Präfix u8. Die Zeichen, die es beinhaltet sind UTF-8 kodiert. Da ein einzelnes UTF-8 kodiertes Byte in einem gewöhnlichen char gespeichert werden kann, sind UTF-8 kodierte String-Literale vom Typ "Array von char ".
    • UTF-16 String-Literale. Eingeleitet werden diese mit dem Präfix u. Die dahinterstehende Kodierung ist UTF-16 und der Typ der Literale ist "Array von char16_t ". char16_t ist ein neu eingeführter Typ mit der Kodierung UTF-16, der im Header <cuchar> definiert ist.
    • UTF-32 String-Literale. Sie beginnen mit einem U. Die Kodierung ist UTF-32 und der Typ ist "Array von char32_t ". char32_t ist ebenfalls in <cuchar> definiert.
    • Rohe String-Literale. Zu jeder der aufgeführten Arten gibt es korrespondierende "rohe" String-Literale, die vor allem den Umgang mit Regular-Expressions vereinfachen sollen. Innerhalb solcher Literale ist es nicht notwendig, einen Schrägstrich oder Anführungszeichen zu escapen. Diese String-Literale enthalten in ihrem Präfix ein "R", also entweder LR, u8R, uR, UR oder nur R.

    Beispiel 1 - Neue String-Literale

    char const* ordinary_literal = "Next comes the realist phase";
    char const* utf8_literal = u8"After all,";
    wchar_t const* wide_char_literal = L"from a purely geometrical point of 
    view";
    
    char16_t const* utf16_literal = u"a cat is only a tube";
    char32_t const* utf32_literal = u"with a door at the top.";
    
    char const* raw_literal = R"[by
    "Terry Pratchett"
    ]";
    
    assert(strcmp(raw_literal, "by\n\"Terry Pratchett\"") == 0);
    
    char const* raw_utf8 = u8R"**[...]**";
    char16_t const* raw_utf16 = uR"*@[...]@*";
    char32_t const* raw_utf32 = UR"zzz[...]zzz";
    

    Erläuterung
    Vor allem rohe String-Literale bieten einiges an Neuerungen: Sie beginnen nicht einfach mit doppelten Anführungszeichen, ihr eigentlicher Inhalt ist zwischen zwei eckigen Klammern. Innerhalb dieser eckigen Klammern müssen Zeichen wie der Backslash und Anführungszeichen nicht durch einen Backslash eingeleitet werden.

    R"[The Great Zaganza said: "You are very fat and stupid and
    persistently wear a ridiculous hat which you should be ashamed of."]";
    

    Merke: Innerhalb von "[ und ]" müssen die Anführungszeichen in der direkten Rede nicht als \" eingegeben werden. Newlines in der Quelldatei werden eins-zu-eins im String-Literal nachgebildet. Das kann man allerdings mit einem Backslash am Ende der Zeile, ganz so wie bei Makros, verhindern.

    Um die Verwendung zu vereinfachen ist es außerdem möglich, zwischen " und [ bis zu sechszehn Zeichen zu setzen. Damit lässt sich ein rohes String-Literal auch schreiben, wie folgt.

    u8R"-->[Der Inhalt]-->";
    

    Folgender String kann mithilfe roher String-Literale einfacher ausgedrückt werden
    Beispiel 3 - HTML in String-Literalen

    "<HTML>\n"
    "<HEAD>\n"
    "<TITLE>Auto-generated html formated source</TITLE>\n"
    "<META HTTP-EQUIV=\"Content-Type\" CONTENT=\"text/html; charset=windows-1252\">\n"
    "</HEAD>\n"
    "<BODY LINK=\"#0000ff\" VLINK=\"#800080\" BGCOLOR=\"#ffffff\">\n"
    "<P> </P>\n"
    "<PRE>\n"
    
    //Mit einem rohen String-Literal sieht das ganze so aus:
    R"[\
    <HTML>
    <HEAD>
    <TITLE>Auto-generated html formated source</TITLE>
    <META HTTP-EQUIV="Content-Type" CONTENT="text/html; charset=windows-1252">
    </HEAD>
    <BODY LINK="#0000ff" VLINK="#800080" BGCOLOR="#ffffff">
    <P> </P>
    <PRE>
    ]"
    

    Der Sinn der Zeichenkette zwischen " und [ liegt darin, dass man sich jegliche Escape-Sequenzen erspart. Man stelle sich einen String vor, der die Zeichenfolge ]" beinhalten soll. Mit rohen String-Literalen kein Problem:

    R"--[Dieser String kann über ]" sprechen]--";
    

    Will man nun über ]" und ]--" sprechen, ist das dank dieser Zeichenkette ebenfalls möglich:

    R"-*-[Dieser String spricht über ]" und ]--" als wäre nichts]-*-";
    

    Neben String-Literalen gibt es in C++ auch Ganzzahl- und Gleitkommazahl-Literale, beispielsweise ist 1024L mit dem Suffix "L" automatisch vom Typ long int . Es wird in C++09 eventuell möglich sein, auch selbstdefinierte Suffixe für eigene Literale zu erschaffen. Damit ist zum Beispiel die Implementation einer Ganzzahlklasse möglich, die beliebig große Zahlen aufnehmen kann.

    Beispiel 3 - Selbstdefinierte Literale

    //So etwas wäre möglich:
    123.4567890123df //dezimale Gleitkommazahl (realisiert in Klasse DecimalFloat)
    "hello!"s        //std::string
    3.4i             //std::complex
    
    10110101b        //binäre Zahl
    123km            //Kilometer
    54385839210349381058031481309349031big
                     //Zahl mit beliebigen Bereich
    
    unsigned long operator "b" (char const*);
    
    template <char...> unsigned long operator "bb" ();
    

    Erläuterung
    T operator "X" (char const*) ist ein Literal-Operator. In obigem Beispiel ist unsigned long operator "b" (char const*) einer. Dieser Operator wird aufgerufen mit 10110101b; - das wird übersetzt in folgenden Funktionsaufruf:

    operator "b"("10110101");
    

    Zusätzlich gibt es eine Literal-Operatoren-Form, die auf Variadic Templates basiert. Im Beispielcode ist operator "bb" auf diese Weise implementiert. Dieser Operator wird wie folgt aufgerufen:

    1234bb;
    //wird zu
    operator "bb" <'1', '2', '3', '4'> ()
    

    In Kombination mit constexpr erlaubt dies die Zusammensetzung von Werten zur Compile-Zeit.

    Status
    Die neuen String-Literale sind bereits im Working-Draft des kommenden C++-Standards enthalten. Selbstdefinierte Literale hingegen sind zwar von der Standardkommission erwünscht, jedoch noch nicht besprochen worden. Dennoch gilt der Vorschlag als ausgereift und wird sehr wahrscheinlich im kommenden Standard integriert sein.

    Unterstützung
    Mir sind keine Compiler bekannt, die dieses Feature unterstützen.

    Proposals
    N2146 - Rohe String-Literale (Beispiele)
    N2209 - UTF-8 String-Literale (Beipsiele)
    N2442 - Unified Proposal
    N2249 - Neue primitive Typen
    N2378 - Selbstdefinierbare Literale

    2 Bewährtes verbessert

    Einige der bestehenden Sprachmöglichkeiten werden durch den neuen Standard so erweitert, dass sie entweder einfacher einzusetzen (die neue for -Schleife, right-angle-brackets, …), aussagekräftiger ( deleted -Funktionen statt private Copy-Konstruktoren, konstante Ausdrücke, nullptr statt NULL , …) oder typsicher ( enums und nullptr ) sind, oder so dass ihre Semantik auf andere Sprachkonstrukte ausgedehnt wird ( template -Aliases, explizite Umwandlungsoperatoren, …).

    2.1 Templates neu

    Im Bereich der generischen Programmierung hat sich einiges getan. Die bereits erwähnten Variadic Templates (1.1) machen dabei sicher einen großen Teil aus, aber sind bei weitem nicht alles.

    Zu den Verbesserungen, die Templates betreffen, gehören:

    • Template Aliases (aka Template Typedefs). Erlaubt das Einführen von typedef -Namen über eine sogenannte alias-Deklaration mithilfe von using .
    • Extern Template. Verhindert die (implizite) Instanziierung eines Templates. Die Instanziierung muss in einer anderen Übersetzungseinheit stattfinden.
    • Erweiterte friend -Deklaration. Erlaubt die Deklaration friend class T; innerhalb eines Templates mit dem Template-Parameter T.
    • Right Angle Brackets. >> wird im Kontext von Templates nicht mehr als Right-Shift-Operator interpretiert.
    • Default-Templateargumente für Funktionstemplates.

    Beispiel

    using foo = int;                   //Alias-Deklaration.
    
    template <class T, class U>
    struct Foo;
    
    template <class T>
    using SameFoo = Foo<T, T>;
    
    SameFoo<int> x;                    //Identisch zu Foo<int, int>.
    
    //Verhindert die implizierte Instanziierung von
    //Foo<double, int> in dieser Übersetzungseinheit
    extern template Foo<double, int>;
    
    void take_foo (Foo<double, int> foo)
    {
    //Foo<double, int> wird hier nicht instanziiert.
    }
    
    #include <vector>
    
    void bar ()
    {
       vector<vector<int>> vvi;         //Beachte: Kein Abstand zwischen > und >
    }
    
    template <class T = int>            //Nicht legal in C++03
    void quux (T t) {}
    
    template <class T>
    class no_children
    {
       no_children () {}
       friend class T;                  //Nicht legal in C++03
    };
    
    class bachelorette
    : virtual no_children<bachelorette> //eine "final"-class in C++09
    {
       friend Foo;                      //friend class Foo ist nicht nötig
       friend class girlfriend;         //Vorwärtsdeklaration inklusive
    };
    
    class girlfriend                    //Hat Zugriff auf alle privaten Daten von bachelorette
    {
    };
    

    Status
    Alle diese Features sind bereits im Working-Draft des kommenden C++-Standards enthalten.

    Unterstützung
    Comeau unterstützt die erweiterte friend -Deklaration, GCC 4.3. unterstützt Right Angle Brackets und Default-Templateargumente für Funktionstemplates.
    Comeau, Borland C++, GCC, IBM, Metrowerks, Microsofts C++ Compiler unterstützen bereits alle extern -Templates.

    Proposals
    N1489 - Alias-Deklarationen (Hintergrund)
    N1448 - Extern Templates (Hintergrund)
    N1520 - Erweiterte friend -Deklaration (Hintergrund)
    N1757 - Right-Angle-Brackets

    2.2 Konstruktion von Objekten

    Neben der bereits angeführten Möglichkeit, Konstruktoren einzuführen, die die Move-Semantik (1.2) unterstützen, gibt es einiges Neues bei Elementinitialisierungslisten und der Art, wie Konstruktoren aufgerufen werden können.

    • Delegierte Konstruktoren. Es ist nun möglich, Konstruktoren aus anderen Konstruktoren aufzurufen
    • Forward Constructors. Vorwärtskonstruktoren (vererbte Konstruktoren) geben ihre Argumente direkt an die entsprechenden Konstruktoren der Basisklasse weiter.
    • Einheitliche Initialisierung. Die Syntax für die Initialisierung von Arrays wird ausgeweitet.
    • std::initializer_list<...> - Initialisierungslisten auch für komplexe Klassen.
    • Direkte Initialisierung von nicht-statischen, nicht-konstanten Klassenvariablen

    Beispiel

    class Foo
    {
       int a = 42;                    //direkte Initialisierung nicht-statischer,
       int b = 23;                    //nicht-konstanter Elementariablen und
       int array[] = { 1, 2, 3 };     //sogar von Arrays!
    
    public:
       Foo () 
       : a(12), array{ 4, 5, 6 } {}   //a = 12, b = 23, array = { 4, 5, 6 }
       Foo (int x) : a(x) {}          //a = x, b = 23
       Foo (int x, int y) 
       : a{x}, b{y} {}                //einheitliche Initialisierung mit {}
    };
    
    #include <initializer_list>
    
    class Bar : public Foo
    {
    public:
       //Vorwärtskonstruktoren:
       using default Foo;             //Deklariert Bar(), Bar(int) und Bar(int, int);
    
       Bar (std::initializer_list<int> list) 
       {
          //Compiler-Magie: list.size() ist als constexpr deklariert (siehe 2.3)
          int elems[list.size()];    
          std::copy (list.begin(), list.end(), elems);
       }
    };
    
    Bar b = { 1, 2, 3, 4, 5 };        //Verwende die std::initializer_list
    
    Bar foo (Bar b)
    {
       return { 10, 11, 12, 13, 14 };
    }
    
    foo( {6,7,8,9} );                   
    
    #include <vector>
    
    int main ()
    {
       //Auch std::vector unterstützt std::initializer_list
       std::vector<Bar> v 
       {
          { 1, 2, 3, 4 },
          { 5, 6, 7 },
          { 8, 9 }
       };
    }
    

    Erläuterung
    std::vector kann in Kombination mit std::initializer_list letzten Endes wie ein herkömliches Array verwendet werden. Womöglich wird sich die einheitliche Form, Variablen zu initialisieren: int a { 5 }; wegen ihrer Uniformität durchsetzen.

    Status
    Delegierte Konstruktoren sind bereits im Working-Draft des kommenden C++-Standards enthalten. Die anderen Features sind in der finalen Revisionsphase, an ihnen werden nur noch letzte Schliffe in der Formulierung durchgeführt, bevor sie in den Working-Draft aufgenommen werden.

    Unterstützung
    Mir sind keine Compiler bekannt, die diese Features unterstützen.

    Proposals
    N2215 - einheitliche Initialisierung und Initialisierungslisten (Hintergründe)
    N1986 - Delegierte Konstruktoren
    N2376 - Vererbte Konstruktoren

    2.3 Konstante Ausdrücke

    Konstante Ausdrücke in C++ sind Ausdrücke, die bereits zur Compile-Zeit ausgewertet werden. Konstante Ausdrücke können beispielsweise bei der Definition eines nicht-dynamischen Arrays verwendet werden. Ein Drawback ist, dass C++03 vorschreibt, dass ein Ausdruck, in dem eine Funktion aufgerufen wird, kein konstanter mehr ist. C++09 ändert diesen Sachverhalt: Wird eine Funktion mit dem Schlüsselwort constexpr deklariert, kann sie dennoch als Teil eines konstanten Ausdrucks verwendet werden.

    Beispiel

    const int size = 42;
    int array1[size];
    
    constexpr int square (int x) { return x*x; }
    int array2[square(4)];
    

    Erläuterung
    Vor allem Funktionen wie numeric_limits<T>::max() können nun in konstanten Ausdrücken verwendet werden.
    Damit eine Funktion in einem konstanten Ausdruck vorkommen kann, …

    • … muss sie als constexpr deklariert werden,
    • … darf sie nur ein return expr; Statement enthalten,
    • … darf sie nur bereits vollständige definierte Funktionen aufrufen (d.h. nicht sich selbst).

    Status
    Dieses Features ist bereits Teil des Working-Drafts für den kommenden C++-Standard.

    Unterstützung
    Meines Wissens wird dieses Feature noch von keinem Compiler unterstützt.

    Proposals
    N2235 - Konstante Ausdrücke (Hintergründe)

    2.4 for für Ranges

    Oftmals muss mit einer for-Schleife einen Bereich [begin, end[ durchgehen. Dies geschieht meist mit Iteratoren, deren Definition in for-Schleifen schon durch das Schlüsselwort auto vereinfacht wurde. Mithilfe von Konzepten (1.3) ist es jetzt auch möglich, die Semantik einer neuen for-Schleife zu beschreiben, die über einen bestimmten Bereich iteriert.

    Das Konzept "Range" ist in <iterator_concept> definiert wie folgt:

    concept Range <typename T>
    {
       InputIterator iterator;
       iterator begin (T&);
       iterator end (T&);
    };
    

    Für Arrays, die Standardcontainer und std::initializer_list sind jeweils concept_maps definiert, die es erlauben, sie als Range zu betrachten.

    Beispiel - Anwendung der neuen for-Schleife

    #include <iterator_concept>
    
    template <class T>
    void test (T range)
    {
       for (auto& element: range)
       {                           
          x *= 2;
       }
    }
    

    Erläuterung
    Die Variable range muss das Konzept Range unterstützen (oder es muss eine passende Concept-Map bereitgestellt werden). Intern wird ein Iterator definiert, der mit Range<decltype(range)>::begin(range) initialisiert wird und mit jeden Schleifendurchlauf inkrementiert wird, bis er Range<decltype(range)>::end(range) erreicht. Das Element, auf das dieser Iterator zeigt, kann man dann jeweils über die Schleifenvariable element ansprechen. Das Range, über das for iteriert, sollte dabei nicht verändert werden.

    Status
    Dieses Feature ist noch nicht im Working-Draft des kommenden C++-Standards enthalten. Es befindet sich in der finalen Revisionsphase und ist von daher ziemlich komplett. Nur einige Formulierungen müssen noch an den Standard angepasst werden.

    Unterstützung
    Mir sind keine Compiler bekannt, die dieses Feature unterstützen.

    Proposals
    N2196 - Einführung und Formulierung
    N2394 - Aktuelle Formulierung

    2.5 Explizite Umwandlungsoperatoren

    Um die implizite Konvertierung in einen selbstdefinierten Typen durch einen Konstruktor zu verhindern, wird in C++03 das Schlüsselwort explicit verwendet. In manchen Situationen ist es jedoch nötig, die Typumwandlung nicht von einem Konstruktor, sondern von einem speziellen Operator zur Typumwandlung durchführen zu lassen. Dies ist beispielsweise der Fall, wenn man keinen Zugriff auf die Implementation der Zielklasse hat. Auch Smart-Pointer verwenden diese Möglichkeit, um beispielsweise einen operator bool zur Verfügung zu stellen. Smart-Pointer wie boost::shared_ptr geben in Wahrheit allerdings einen Typen zurück, der auch im Kontext von boolschen Werten verwendet werden kann, meistens einen Zeiger auf eine Elementvariable, denn würden sie einen boolschen Wert direkt zurückgeben, könnte dieser vom Compiler in Ausdrücken weiter umgewandelt - und letzten Endes falsch verwendet werden. Der std::shared_ptr wird daher einen explicit operator bool haben. Um den hässlichen Workaround mit Zeigern auf Elementvariablen zu umgehen, wird außerdem ein " bool -Kontext" eingeführt werden, in dem explizite Umwandlungsoperatoren nach bool automatisch aufgerufen werden. Dies geschieht im Kopf einer Schleife und in der Bedingung eines if -Statements.

    Beispiel

    template <class T>
    struct ptr
    {
       //… 
       explicit operator bool () { return pointee; }
       T *pointee;
    };
    
    void foo (ptr x)
    {
       //Automatische Konvertierung nach bool
       if (x);
    
       //Kein bool-Kontext, daher: Fehler
       if (x < 42);
    }
    

    Status
    Dieses Feature ist Teil des Working-Drafts des kommenden C++-Standards.

    Unterstützung
    Mir sind keine Compiler bekannt, die dieses Feature unterstützen.

    Proposals
    N2437 - Explizite Umwandlungsoperatoren (Hintergründe)

    2.6 Typsicheres enum

    Aufzählungen ( enums ) in C++ haben alle möglichen Ecken und Kanten.

    • enums sind nicht vollständig typsicher. Es ist zwar nicht möglich, eine Zuweisung von einem enum -Typen zu einem anderen durchzuführen und es gibt auch keine implizite Konvertierung eines int -Wertes zu einem enum -Typen, aber enums können ganz leicht selbst zu int -Werten werden.
    • enums sind intern mit eines Typen implementiert, der nicht explizit angegeben werden kann. Man kann daher nicht wissen, welche Größe eine enum -Variable einnehmen wird. Außerdem ist es nicht möglich zu sagen, ob der zugrundeliegende Typ eines enums signed oder unsigned ist.
    • Die einzelnen Enumeratoren eines enums werden in den Namensbereich des enums selbst eingeführt.

    Um diese Defizite zu umgehen, wird in C++09 eine neue Art enum eingeführt: die enum class .

    Beispiel

    enum class Color { Red, Green, Blue };
    enum class Direction { Left, Down, Right, Up };
    
    Color col = Direction::Left   //Fehler, auch mit einfachen enums
    
    int i = Color::Red;           //Fehler, nur mit einfachen enums erlaubt
    
    Direction dir = Left;         //Fehler, nur mit einfachen enums erlaubt
    
    if (dir >= col)               //Schlimmer Fehler! Einfache enums würden dies allerdings zulassen.
    
    enum class Size : uint32_t { Small, Medium, Big };
    //Implementiert als 32-Bit unsigned Integer.
    //(uint32_t aus dem Standard-Header <cstdint>)
    

    Status
    Dieses Feature befindet sich bereits im Working-Draft des kommenden C++-Standards.

    Unterstützung
    Mir sind keine Compiler bekannt, die dieses Feature unterstützen.

    Proposals
    N2347 - Streng getypte enums (Hintegründe)

    2.7 default-Funktionen

    C++ bietet vier spezielle Elementfunktionen an: Den Destruktor, den Standardkonstruktor, den Kopierkonstruktor und den Zuweisungsoperator. Diese Elementfunktionen werden vom Compiler automatisch erstellt, können jedoch jederzeit überschrieben werden.

    Außerdem definiert C++ diverse globale Operatoren auch für selbstdefinierte Typen: operator , (Der Sequenzoperator), operator & (Der Adress-Operator), operator * , operator -> , operator ->* , operator new und operator delete .

    Die Herangehensweise von C++03 hat jedoch einige Probleme: Deklariert man beispielsweise einen beliebigen, selbstdefinierten Konstruktor, wird kein Standardkonstruktor angelegt, implizite Destruktoren funktionieren nicht mit polymorphen Klassen, und selbstdefinierte Implementationen sind nicht-trivial, verhindern also POD-Semantik.

    Diese Probleme fallen besonders dann auf, wenn man versucht zu verhindern, das eine Klasse kopierbar ist: durch die Deklaration eines privaten Kopierkonstruktors und Zuweisungsoperators ohne Definition. Dadurch ist der Typ allerdings kein POD mehr und zusätzlich muss man einen eigenen Standardkonstruktor definieren.

    In C++09 ist es nun möglich explizit zu sagen, dass man für eine Klasse einen compilergenerierten Standardkonstruktor bekommen möchte - auch dass man einen bestimmten Konstruktor nicht haben will, ist einfach ausdrückbar.

    Beispiel

    struct Foo
    {
       Foo() = default;           //Standardkonstruktor explizit verlangt
       virtual ~Foo () = default; //Implementation dem Compiler überlassen
       Foo (Foo const&);
    };
    
    inline Foo::Foo (Foo const&) = default;
    
    class Bar
    {
    public:
        Bar () = default;
    
        Bar& operator = (Bar const&) = delete;
        Bar (Bar const&) = delete;
        void* operator new (std::size_t) = delete;
    };
    
    void quux (long long);
    void quux (long) = delete;
    

    Erläuterung
    Die Klasse Bar beschreibt einen Typen, der nicht kopierbar ist, da Kopierkonstruktor und Zuweisungsoperator gelöscht sind. Außerdem kann Bar nicht dynamisch mit new erzeugt werden, da auch operator new für Bar nicht existiert.

    Auch freistehende Funktionen können gelöscht werden: Dies verbietet bei quux zum Beispiel eine Typumwandlung von long nach long long . Kurz gesagt: Eine Überladung für long wird zwar gefunden (Die Version mit long long wird nicht verwendet), deren Definition ist aber gelöscht, was schon zur Compile-Zeit einen Fehler verursacht.

    Status
    Dieses Feature ist bereits im Working-Draft des kommenden Standards enthalten.

    Unterstützung
    Mir sind keine Compiler bekannt, die dieses Feature unterstützen.

    Proposals
    N2346 - defaulted and deleted Functions (Hintegründe)

    2.8 Plötzlich POD

    POD ist die Abkürzung für plain-old-data und ist im C++-Standard von 2003 ziemlich streng definiert: Eine POD-Struktur ist eine Klasse, die nur PODs als Elementvariablen enthalten kann, die keinen selbstdefinierten Zuweisungsoperator, keine selbstdefinierten Konstruktoren und keinen selbstdefinierten Destruktor enthalten darf, die weiters keine privaten oder geschützten ( protected ) Elementvariablen aggregiert, keine Basisklassen hat und auch keine virtuellen Funktionen.

    Es gibt demgegenüber jedoch einige Vorteile, für die es sich lohnt, PODs einzusetzen:

    • PODs sind byteweise kopierbar. Optimierungen mit std::memcpy sind dadurch möglich.
    • PODs sind mit Strukturen aus C layout-kompatibel.
    • Spezifische Initialisierungs-Garantien. Multithreaded Programme können data races während der Initialisierung von PODs umgehen.

    Ein klassisches Beispiel, dass die Absurdität dieser strengen Definition veranschaulicht:

    struct A
    {
       int n;
    };
    
    struct B
    {
       int n;
       B(int n_) : n(n_) {}
    };
    

    In C++03 ist A eine POD-Struktur, B ist keine.

    C++09 verändert die Definition von POD wie folgt:

    • PODs dürfen eigene Konstruktoren haben, solange der triviale Standardkonstruktor und die anderen speziellen Elementfunktionen verfügbar sind.
    • PODs dürfen Basisklassen haben, diese dürfen allerdings nicht polymorph sein. Eine der Basisklassen darf nicht-statische Elementvariablen enthalten, wenn die abgeleitete Klasse keine hat.
    • PODs haben Zugriffskontrolle. Allerdings müssen alle Elementvariablen denselben Zugriff erlauben.
    • PODs sind nun unabhängig von Aggregattypen definiert, deren eigentlicher Zweck es ist, eine spezielle Art von Initialisiation zu Verfügung zu stellen. PODs sind stattdessen nun in Begriffen von trivialen Typen und Standard-Layout-Typen definiert. Triviale Klassen haben triviale spezielle Memberfunktionen (Standardkonstruktor, Kopierkonstruktor, Destruktor, Zuweisungsoperator), was virtuelle Funktionen und virtuelle Basisklassen ausschließt. Standard-Layout-Klassen haben nur Elementvariablen, die ebenfalls Standard-Layout-Klassen sind; sie haben weder virtuelle Funktionen noch virtuelle Basisklassen, dieselben Zugriffsrechte für alle Elementvariablen, besitzen nur Standard-Layout-Basisklassen, welche nur dann Elementvariablen beinhalten dürfen, wenn die von ihnen abgeleitete Klasse keine hat.

    PODs sind dabei Standard-Layout-Typen und triviale Typen. Der Grund, diese neuen Definitionen einzuführen liegt darin, dass man erkannt hat, dass an vielen Stellen im alten Standard der Begriff POD semantisch nicht korrekt war und sich entweder auf triviale Typen oder auf Standard-Layout-Typen bezog.

    Durch die neue Definition werden einige Typen zu POD-Typen, die es davor nicht waren. Vor allem erlaubt es eine Änderung von std::pair , das danach eine POD-Struktur sein kann.

    Status
    Dieses Feature ist bereits im Working-Draft des kommenden C++-Standards enthalten.

    Unterstützung
    Mir ist kein Compiler bekannt, der dieses Feature unterstützt.

    Proposals
    N2342 - PODs unstrung (Hintergründe)

    2.9 nullptr

    Der Wert 0 kann in C++ verschiedenes bedeuten. Einerseits ist er natürlich eine Ganzzahl mit dem Wert 0, andererseits kann 0 aber auch in einen Zeiger, den sogenannten Null-Zeiger umgewandelt werden. Dies schafft Probleme bei der Funktionsüberladung.

    void f(int);
    void f(char*);
    
    f(0);                   //Ruft f(int) auf, niemals f(char*)
    
    std::string s1(false);  //Kompiliert tadellos - ruft den char const*-Konstruktor mit dem Null-Zeiger auf
    std::string s2(true);   //Fehler
    

    Das Proposal, dass nullptr vorschlägt (n2431) fasst dieses unintuitive Verhalten wie folgt zusammen:

    A name for the null pointer: nullptr (revision 4), Proposal No. 2431 schrieb:

    An alternative description of this effect might be: "0 is always both an integer constant and a null pointer constant, except when it's not."

    Auch wenn C++09 aus Gründen der Code-Kompatibilität nicht auf die implizite Umwandlung von 0 in den Null-Zeiger verzichten will, wird ein neuer Null-Zeiger, genannt nullptr , eingeführt. nullptr hat dabei einen eigenen Typ, der über ein typedef in <cstddef> definiert ist:

    typedef decltype(nullptr) nullptr_t;
    

    nullptr_t ist ein POD-Typ, konvertierbar in Zeiger-Typen und Zeiger-auf-Member-Typen. Ansonsten kann ein Objekt von diesem Typ in keinen anderen Typen umgewandelt werden, weder in integrale Typen noch in den bool -Typen.

    Beispiel

    char *a = nullptr;
    char *b = 0;
    int n = nullptr;        //Fehler
    
    if (a == b);            //Bedingung erfüllt
    if (a == 0);            //Bedingung erfüllt
    if (n == nullptr);      //Fehler
    
    char *c = expr ? nullptr : nullptr;   
    char *d = expr ? 0 : nullptr;          //Fehler: Typen sind nicht kompatibel.
    
    sizeof (nullptr);       //OK
    typeid (nullptr);       //OK
    throw nullptr;          //OK
    
    void fun (char*);
    void fun (int);
    
    fun(nullptr);             //Ruft fun(char*) auf
    fun(0);                   //Ruft fun(int) auf
    
    template <typename T>
    void bar (T t);
    
    bar(0);                   //T == int
    bar(nullptr);             //T == nullptr_t
    bar((float*)nullptr);     //T == float*
    

    Status
    Dieses Feature ist bereits im Working-Draft des aktuellen C++-Standards enthalten.

    Unterstützung
    Mir sind keine Compiler bekannt, die dieses Feature unterstützen.

    Proposals
    N2431 - nullptr (Hintergründe)

    3 Ausblick

    Im zweiten Teil dieser Serie möchte ich in ähnlicher Art und Weise wie hier einen Überblick über Neuerungen in der C++-Standardbibliothek geben. Die Standardbibliothek ist wohl der Teil des Standards, der sich am gröbsten erweitert hat: Von knapp unter 400 Seiten im Standard von 2003 zu über 700 Seiten im aktuellen Working-Draft, und dabei sind einige der neuen Librarys noch gar nicht mit dabei!

    4 Quellen und Verweise

    Direkte Quellen
    Das C++ Standardisierungs Komitee
    Der aktuelle Working-Draft
    Alle Proposals nach Jahr
    Offizieller Status der C++-Entwicklung
    Die allerneuesten Proposals

    Verweise
    Quick'n'dirty - An Introduction to C++0x (November 2007)
    A Brief Look at C++0x (Januar 2006)
    B. Stroustrup: The Design of C++0x (C/C++ User's Journal, Mai 2005)
    Stroustrup über Initialisierungslisten (youtube.com, Februar 2007)
    H. Sutter: Über die Entwicklung von C++0x (Februar 2007)
    C++-Sprachsupport in der GCC
    Download von ConceptGCC



  • So, der Hauptteil über die neuen Sprachfeatures exkl. Standardbibliothek ist fürs erste komplett. Wenn ich eurer Meinung nach etwas grundlegendes vergessen habe, bzw. ein bestimmtes Thema anders angehen/strukturieren soll, oder irgendetwas ausführlicher darstellen, bitte schreiben.

    Den Teil mit TR1 und der Multithreading-Bibliothek möchte ich eher kurz halten, hauptsächlich Beispiele, die die Funktionsweise der neuen Klassen veranschaulichen.

    Vielleicht schaffe ich es ja noch, bevor ich allzu müde werde 🙂

    /edit: OK, ich hab's und ich bin geschafft.
    die einzig gröberen änderungen, die jetzt noch kommen werden, sind wünsche von euch. danach kann der artikel schon in die [T]-phase udn von den C++-Gurus zerfetzt werden 😋



  • Mir gefällt der Artikel sehr gut. 👍

    Du könntest vielleicht noch einige Links zu relevanten Dokumenten einfügen. Wenigstens ein Link zu http://www.open-std.org/JTC1/SC22/WG21/docs/papers/. Ansonsten sehe ich nicht was man da noch gros hinzufügen kann.

    Da der Artikel ja nicht zentral um Variadic Arguments geht würde ich das printf Beispiel raus lassen.



  • ja, ich habe vor, noch eine quellensammlung anzugeben, klar.

    immerhin habe ich mich jetzt auch durch virtuelle tonnen von dokumentation und proposals durchgeackert und muss jetzt erstnoch die relevanten links rausfinden.

    Vor allem http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2007/n2432.html und http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2007/n2433.html waren mir beim Überblick eine große Hilfe.

    Ich weiß nur noch nicht, wie genau.

    Möglichkeiten:
    Nicht allzu viele Links als Endnoten angeben (~an die 10, sonst wird man an Masse erschlagen) - oder bei jedem Absatz ein Link à la Proposal 1757 - Right Angle Brackets bzw. sofern es gute Englische Artikel zu einem Thema gibt (bspw. einen nette einführenden Artikel zum Multitasking-Speichermodell von C++ oder Herb Sutters (oder war's Scott Meyers) Einführungen zu neuen Sprachmiteln)



  • PS was natürlich sehr schön wäre: Wenn es C++0x-Tags gäbe, die zumindest die neuen Schlüsselwörter auch highlighten könnten 🙂



  • Du hast ja eh nach jedem Feature eine Minischlussfolgerung mit "Status", "Unterstützung" und so weiter. Da könntest du noch "Quelle" hinzufügen und dann ein oder zwei Links auflisten.



  • Mach ich, (nach dem Mittagessen), die Stelle über Variadic-Templates hab ich jetzt auch nochmal überarbeitet.



  • Ich geb gleich zu, daß ich einige Teile nur überflogen habe. Aber auf den ersten Blick sieht es schon interessant aus.

    Aber:

    • die rohen String-Literale müsstest du eventuell genauer erklären.
    • ergänzt man mit der Concept-Map eigentlich die Schnittstelle einer Klasse? (Sprich: Könnte ich auch "MyString sum = concatenate(str1,str2);" schreiben?)
    • was genau bedeutet "PODs sind nicht mehr von der Definition von Aggregattypen abhängig."? Kann etwas ein POD sein, was nicht-POD Datenelemente enthält? Wenn ja, wie wird da die Kompatibilität zu C structs sichergestellt?

    (btw, Sachen wie variadic Templates oder automatische Typerkennung klingen auf jeden Fall interessant, bei der neuen Funktionsdefinition bin ich etwas skeptisch (auch wenn sie mich an Pascal (FUNCTION test(...) : int) erinnert - aber das ist eine Sache der Gewohnheit))



  • Vielen Dank für die Arbeit! Endlich mal ne deutsche Übersicht zu den neuen Features. Habe zwar immer englische Texte dazu gelesen, aber so liest es sich deutlich leichter. **zensiert** Ich finde, da sind echt knallharte Neuerungen mit bei, die die anderen Sprachen nicht bekommen würden (wenn dann auch nur alle 10 Jahre).

    Egal, ich finde die Übersicht super.

    Die Library-Neuerungen kann und sollte man ruhig in einen anderen Artikel packen. Sonst wird es auch schwer zu lesen sein, weil man elend lange scrollen muß.



  • Artchi schrieb:

    Auch bekommen endlich mal die Nörgler ("Boh, sind die langsam und das wird nie was!") zu sehen, das nicht wie bei Java u.a Sprachen mit jeder version Mini-Änderungen kommen.

    Kannst du den Scheiss nichtmal in der Redaktion lassen? Echt arm...



  • Ist zensiert.



  • Artchi schrieb:

    Wieso?

    Weil du derjenige bist, der sich immer über Flamer und Trolle aufregt, aber selbst einer der größten Flamer bist. Das ist einfach nur zum kotzen.

    Artchi schrieb:

    Fühlst du dich angesprochen? 🙄

    Wenn du nicht komplett blind durch das Forum laufen würdest, wüsstest du, dass ich C-Vertreter bin. Und da man bei C dasselbe Standardisierungdingens macht kann ich mich hier wohl kaum angesprochen fühlen.

    @ alle anderen. Tut mir leid dass ich das überhaupt kommentiert habe. Ignoriert es am besten 😞



  • Guter Artikel. Allerdings könnte man aus dem Aktuellen Text imho schon gut 2 Artikel machen.



  • CStoll schrieb:

    die rohen String-Literale müsstest du eventuell genauer erklären.

    gesagt getan. das hindernis daran war wohl, dass es diejenige neuerung ist, die mich am wenigsten interessiert ^^' (von den angesprochenen)

    was genau bedeutet "PODs sind nicht mehr von der Definition von Aggregattypen abhängig."? Kann etwas ein POD sein, was nicht-POD Datenelemente enthält? Wenn ja, wie wird da die Kompatibilität zu C structs sichergestellt?

    durch neue definitionen. wenn man im standard "aggregat" hört, bezieht sich das hauptsächlich auf die art, wie es initialisiert werden kann - nämlich wie c-structs mit { 1, 2, 3 }. (das ist erstens wohl sowieso bald überflüssig und geht zweitens ein bisschen an der intention von POD vorbei) - man definiert POD nun über (1) triviale typen und (2) standard-layout-typen. hab das allerdings schon im artikel klarer deutlich gemacht. die definition ist ähnlich geblieben, nur nicht mehr direkt von der definition von aggregat abhängig.

    (btw, Sachen wie variadic Templates oder automatische Typerkennung klingen auf jeden Fall interessant, bei der neuen Funktionsdefinition bin ich etwas skeptisch (auch wenn sie mich an Pascal (FUNCTION test(...) : int) erinnert - aber das ist eine Sache der Gewohnheit))

    mich erinnert die sehr an (S)ML. dort sieht das beinahe gleich aus [ok, beinahe: (int * int) -> int]

    [*]ergänzt man mit der Concept-Map eigentlich die Schnittstelle einer Klasse? (Sprich: Könnte ich auch "MyString sum = concatenate(str1,str2);" schreiben?)

    ich halte ja concepts für die größte neuerung im kernbereich der sprache, gefolgt von variadic templates.

    stroustrup (auf den geht das proposal afaik zurück) hat eine schöne analogie: eine concept-map ist wie die berühmte rosa brille, mit der die welt so gesehen wird, wie man sie braucht. d.h. wenn ein concept eine bestimmte schnittstelle braucht, sieht es sie entweder direkt über die implizite concept-map, die jedes interface darstellt; oder durch eine selbstdefinierte concept-map, die funktionen umbiegt.

    bei der neuen for-schleife habe ich das ein bisschen angeschnitten. diese ist abhängig von einem konzept, dass in <iterator_concept> definiert wird: Range.
    das sieht so aus:

    concept Range <typename T>
    {
        typedef InputIterator iterator;
        iterator begin (T&);
        iterator end (T&);
    };
    

    für arrays und container sind jetzt concept-maps angelegt, die so aussehen:

    template <Container C> //Container ist selbst ein concept
    concept_map Range <C>  //Das ist also eine "Spezialisierung" für alle Container
    {
       typedef C::iterator iterator; //Übersetze in die Begriffe von concept Range
       iterator begin (C& c) { return c.begin(); }
       iterator end (C& c) { return c.end(); }
    };
    

    die for-schleife ist jetzt selbst so implementiert:

    for (Element: range)
    {
       //...
    }
    
    //wird zu:
    typedef decltype(range) range_t;
    
    for (Range<range_t>::iterator i = Range<range_t>::begin(range); i != Range<range_t>::end(range); ++i)
    {
       Element = *i;
       //...
    }
    

    zumindest von der semantik her. Das Range ist über die concept_map für Container spezialisiert und falls range einen container darstellt, dann wird für Range<range_t>::begin(range) einfach range.begin() aufgerufen (steht ja so in der concept_map)

    (in wahrheit ist der code für die expandierte neue for-schleife noch etwas mit rvalue-Referenzen aufgepeppt und optimierter)



  • phlox81 schrieb:

    Guter Artikel. Allerdings könnte man aus dem Aktuellen Text imho schon gut 2 Artikel machen.

    das habe ich mir auch schon überlegt. nur wo? die hälfte des artikels liegt ungefähr bei 2.1., allerdings wäre vom inhalt her eine trennung zwische 1/2 und 3 besser.
    nur leider verweise ich auch quer durch den artikel (von 1.8 auf 3.2 usw.) - also thematisch ist alles irgendwie voneinander abhängig. von daher würde ich mir schwer tun, ihn zu teilen.

    neben der alternative, ihn so zu lassen (es gab auch schon mal artikel in ähnlicher länge), könnte man noch sachen kürzen.



  • queer_boy schrieb:

    phlox81 schrieb:

    Guter Artikel. Allerdings könnte man aus dem Aktuellen Text imho schon gut 2 Artikel machen.

    das habe ich mir auch schon überlegt. nur wo? die hälfte des artikels liegt ungefähr bei 2.1., allerdings wäre vom inhalt her eine trennung zwische 1/2 und 3 besser.
    nur leider verweise ich auch quer durch den artikel (von 1.8 auf 3.2 usw.) - also thematisch ist alles irgendwie voneinander abhängig. von daher würde ich mir schwer tun, ihn zu teilen.

    neben der alternative, ihn so zu lassen (es gab auch schon mal artikel in ähnlicher länge), könnte man noch sachen kürzen.

    da ich nicht die große ahnung von c++ habe, und es mich wohl auch nie interessieren wird 😃 habe ich den artikel auch nur überflogen. ich persönlich finde ihn vom umfang her akzeptabel 🙂



  • queer_boy schrieb:

    CStoll schrieb:

    die rohen String-Literale müsstest du eventuell genauer erklären.

    gesagt getan. das hindernis daran war wohl, dass es diejenige neuerung ist, die mich am wenigsten interessiert ^^' (von den angesprochenen)

    Ja, jetzt wird's klarer - haben die Zeichen zwischen " und [ eigentlich eine Funktion oder werden sie vom Compiler zerstört? (und gibt es eine Möglichkeit, das Endezeichen ] im rohen String zu verwenden (z.B. wenn ich BBCode-Fragmente analog zu deinem HTML-Beispiel unterbringen will)

    was genau bedeutet "PODs sind nicht mehr von der Definition von Aggregattypen abhängig."? Kann etwas ein POD sein, was nicht-POD Datenelemente enthält? Wenn ja, wie wird da die Kompatibilität zu C structs sichergestellt?

    durch neue definitionen. wenn man im standard "aggregat" hört, bezieht sich das hauptsächlich auf die art, wie es initialisiert werden kann - nämlich wie c-structs mit { 1, 2, 3 }. (das ist erstens wohl sowieso bald überflüssig und geht zweitens ein bisschen an der intention von POD vorbei) - man definiert POD nun über (1) triviale typen und (2) standard-layout-typen. hab das allerdings schon im artikel klarer deutlich gemacht. die definition ist ähnlich geblieben, nur nicht mehr direkt von der definition von aggregat abhängig.

    Ja, ist jetzt klarer.

    (btw, Sachen wie variadic Templates oder automatische Typerkennung klingen auf jeden Fall interessant, bei der neuen Funktionsdefinition bin ich etwas skeptisch (auch wenn sie mich an Pascal (FUNCTION test(...) : int) erinnert - aber das ist eine Sache der Gewohnheit))

    mich erinnert die sehr an (S)ML. dort sieht das beinahe gleich aus [ok, beinahe: (int * int) -> int]

    Ja, das kommt davon, womit man angefangen hat 😃 Ich habe als erstes mit Pascal gearbeitet und fand bei der Umstellung die C-Reihenfolge etwas ungewohnt (in Pascal wird der Typ nach dem Namen angegeben), inzwischen habe ich mich daran gewöhnt - und die neue Deklarationsform bringt die Reihenfolge wieder durcheinander (oder darf man auch "i -> int;" schreiben, um Variablen zu definieren?).

    [*]ergänzt man mit der Concept-Map eigentlich die Schnittstelle einer Klasse? (Sprich: Könnte ich auch "MyString sum = concatenate(str1,str2);" schreiben?)

    ich halte ja concepts für die größte neuerung im kernbereich der sprache, gefolgt von variadic templates.

    stroustrup (auf den geht das proposal afaik zurück) hat eine schöne analogie: eine concept-map ist wie die berühmte rosa brille, mit der die welt so gesehen wird, wie man sie braucht. d.h. wenn ein concept eine bestimmte schnittstelle braucht, sieht es sie entweder direkt über die implizite concept-map, die jedes interface darstellt; oder durch eine selbstdefinierte concept-map, die funktionen umbiegt.

    bei der neuen for-schleife habe ich das ein bisschen angeschnitten. diese ist abhängig von einem konzept, dass in <iterator_concept> definiert wird: Range.
    das sieht so aus:
    [...]

    Hm, nach der Erklärung sieht das für mich nach einer Erweiterung von Typ-Traits (ala std::iterator_traits<> oder std::char_traits<>) aus.

    @Aufteilung: Ich würde am ehesten nach Kapitel 2 einen Schnitt machen (btw, bei meiner Serie zur STL hatte ich auch gelegentlich Verweise zwischen den einzelnen Teilen, das ist nicht das Problem ;)).



  • Würde eine Aufteilung auch besser finden. Das Scrollen kann sehr unangenehm sein.

    Habe noch einen Wunsch: kann man evtl. zu lange Codezeilen kürzen bzw. einen Umbruch rein machen? Z.B. sind manche Kommentare hinter einer langen Anweisung (typedef u.ä.), den Kommentar könnte man drüber schreiben. Sonst muß man immer so weit nach rechts scrollen, nicht jeder hat einen 1600x1200 Pixel Monitor.



  • CStoll schrieb:

    Ja, jetzt wird's klarer - haben die Zeichen zwischen " und [ eigentlich eine Funktion oder werden sie vom Compiler zerstört? (und gibt es eine Möglichkeit, das Endezeichen ] im rohen String zu verwenden (z.B. wenn ich BBCode-Fragmente analog zu deinem HTML-Beispiel unterbringen will)

    klar doch:

    R"--[[ubbtag]klar doch:[ubbtag]]--";
    //Was nun, wenn ich die Zeichenkette ]--" in meinem String brauche?
    //Dann mache ich es einfach anders:
    R"**[jetzt kann ich bequem über ]--" schreiben, ohne das etwas passiert]**";
    

    sinn verstanden?
    für die strings zwischen " und [ gibt es nur die Größenbegrenzung: maximal 16 Zeichen - eine gewisse Einschränkung beim Inhalt (keine leerzeichen, kein ", kein [ oder ]) und dass diese sequenz am anfang des strings wie auch am ende dieselbe sein muss.
    hm diese information, soll ich die jetzt noch einbauen (imo habe ich schon genug dazu gesagt), oder nicht (wahrscheinlich wird sich die diskussion, die wir jetzt hier führen dann wiederholen ^^)

    und die neue Deklarationsform bringt die Reihenfolge wieder durcheinander (oder darf man auch "i -> int;" schreiben, um Variablen zu definieren?).

    naja, streng genommen eigentlich nicht, immerhin musst du ja ein auto an vorderste stelle tun...

    Hm, nach der Erklärung sieht das für mich nach einer Erweiterung von Typ-Traits (ala std::iterator_traits<> oder std::char_traits<>) aus.

    ein bisschen mehr als type_traits.

    ein weiteres beispiel:

    concept Stack <typename X>
    {
       typename value_type;
    
       void push (X&, value_type const&);
       void pop (X&);
    
       value_type top (X const&);
       bool empty (const X&);
    };
    
    //vector als Stack:
    template <class T>
    concept_map Stack <std::vector<T>>
    {
       typedef T value_type;
    
       void push (std::vector<T>& v, T const& x) { v.push_back(x); }
       void pop (std::vector<T>& v) { v.pop_back(); }
       T top (std::vector<T> const& v) { return v.back(); }
       bool empty (std::vector<T> const& v) { return v.empty(); }
    };
    
    //de factor sollte die template-concept-map aber auch mit list und deque 
    //funktionieren
    //vector, list und deque unterstützen alle das konzept:
    concept BackInsertionSequence <typename X> 
    {
       typename value_type = X::value_type;
       void X::push_back (value_type const&);
       void X::pop_back ();
       value_type& X::back ();
       const value_type& X::back() const;
       bool X::empty() const;
    };
    
    //und damit lässt sich eine concept-map über ein concept definieren:
    template <BackInsertionSequence X>
    concept_map Stack<X>
    {
    //wie oben...
    };
    

    damit lässt sich quasi ein generisches interface im sinne von std::stack definieren, dass aber - jederzeit erweiterbar - mit x-beliebigen typen, die zunächst gar nicht kompatibel aussehen, funktionieren.
    mit template-type-traits wäre das alles ein ziemlicher aufwand.

    aber concepts können noch mehr: (und das habe ich in der einführung gar nicht erwähnt) sie unterstützen "axiome"

    concept CopyConstructible <typename T>
    {
       T::T (T const&);
    
       axiom CopyEquivalence (T x)
       {
          T(x) == x; //allerdings nur type-checking, nicht semantisch
       }
    }
    

    axiome können zu enormen optimierungen führen: wenn ein axiom aussagt, dass zwei ausdrücke gleich sind, kann der compiler sie gegeneinander austauschen:

    concept Monoid <typename Op, typename T>
    {
       T identity_element (Op);
    
       axiom Identity (Op T x)
       {
          op (x, identity_element(op)) == x;
          op (identity_element(op), x) == x;
       }
    };
    
    template <typename Op, typename T> requires Monoid<Op, T>
    T identity (Op const& op, T const& t)
    {
        return op(t, identity_element(op)); //erfüllt das axiom Identity
        //damit kann der compiler diesen funktionsaufruf durch:
        return t; //ersetzen.
    }
    

    das kann auf jedenfall interessant werden.

    /edit 1, 2, 3 und 4: die verwendung von R"[ ]" könnte in diesem forum noch problematisch werden ^^'



  • @rohe Strings - zumindest solltest du dazuschreiben, welche Aufgabe diese erweiterten Trennzeichen haben ("um die Verwendung zu vereinfachen" ist imho etwas mager).
    (PS: Ja, mit den letzten Erklärungen habe ich den Sinn verstanden)

    und die neue Deklarationsform bringt die Reihenfolge wieder durcheinander (oder darf man auch "i -> int;" schreiben, um Variablen zu definieren?).

    naja, streng genommen eigentlich nicht, immerhin musst du ja ein auto an vorderste stelle tun...

    Von daher ist es vielleicht ein wenig inkonsequent - Pascal verwendet durchgängig die Schreibweise "name:typ", C++ bislang durchgängig "typ name". Allerdings weiß ich auch nicht, wie man das besser lösen könnte.
    (im ARM habe ich afair mal gelesen, daß Template mal als "T template<typename T> func(...)" angedacht waren, wurde zugunsten der leichteren Compilierbarkeit verworfen)


Anmelden zum Antworten