~/blog/2011/09/cpp11-ist-offiziell-was-aendert-si.md

C++11 ist offiziell – was ändert sich?

4 min
Titelbild: C++11 ist offiziell – was ändert sich?

Keine Zeit? Hier die Kurzversion in 30 Sekunden.

  • Der neue C++-Standard wurde am 12. August 2011 einstimmig angenommen; die Veröffentlichung als ISO/IEC 14882:2011 folgt noch. Aus C++0x wird damit C++11.
  • auto, Range-based for, Lambdas und nullptr machen den Alltagscode kürzer und lesbarer.
  • std::unique_ptr und std::shared_ptr ersetzen das alte auto_ptr und nehmen einem viel manuelles delete ab.
  • Move-Semantik und std::thread sind die größeren Brocken, hier nur ein erster Blick.
  • Compiler: GCC 4.6 und Visual C++ 2010 können schon Teile, aber nicht alles. Der bcc32 aus dem C++Builder XE kann davon nur wenig.

Lange hat man vom „C++0x“ gesprochen, weil niemand wusste, ob der Standard noch in den 2000ern fertig wird. Jetzt ist es entschieden: Am 12. August 2011 wurde der neue Standard einstimmig angenommen, die offizielle Veröffentlichung als ISO/IEC 14882:2011 kommt noch. Er heißt ab jetzt C++11. Ich beschäftige mich dieses Jahr wieder mehr mit C++, deshalb hier ein Überblick über das, was mir im Alltag am meisten auffällt. Mit den Sachen, die ich ausprobiert habe, kann man schon ganz gut arbeiten.

Kürzer schreiben: auto, for und nullptr

Mit auto leitet der Compiler den Typ aus dem Initialisierer ab. Das lohnt sich vor allem bei Iteratoren, die man vorher ewig ausschreiben musste. Zusammen mit der neuen for-Schleife über Container wird aus einem Monstrum eine Zeile:

#include <iostream>
#include <map>
#include <string>
#include <vector>

int main()
{
    std::vector<std::string> namen;
    namen.push_back("Anna");
    namen.push_back("Ben");

    // alt:
    // for (std::vector<std::string>::iterator it = namen.begin(); it != namen.end(); ++it)

    for (const auto& name : namen) {
        std::cout << name << std::endl;
    }

    auto zahl = 42;          // int
    auto text = std::string("hallo");
    std::map<int, std::string> m;
    auto it = m.find(1);     // kein langer Iterator-Typ mehr
    return 0;
}

Dazu kommt nullptr. Das alte NULL ist in C++ nur eine 0, und bei überladenen Funktionen führt das zu Überraschungen, weil 0 lieber als int genommen wird als als Zeiger. nullptr hat einen eigenen Typ und ist eindeutig ein Nullzeiger. Ich benutze es inzwischen überall.

Lambdas

Anonyme Funktionen direkt an der Stelle, wo man sie braucht. Das ist besonders mit den Algorithmen aus <algorithm> praktisch, wo man sonst extra eine Funktion oder ein Funktorobjekt schreiben musste:

#include <algorithm>
#include <vector>

std::vector<int> zahlen;
zahlen.push_back(5);
zahlen.push_back(12);
zahlen.push_back(7);

int grenze = 6;
int anzahl = std::count_if(zahlen.begin(), zahlen.end(),
                           [grenze](int x) { return x > grenze; });

In den eckigen Klammern steht, was aus der Umgebung „eingefangen“ wird, hier grenze als Kopie. Mit [&] bekommt man alles per Referenz, mit [=] alles per Kopie. Bei Referenzen muss man aufpassen, dass die Variable noch lebt, wenn das Lambda später aufgerufen wird.

Smart Pointer

std::auto_ptr war schon immer etwas heikel, weil das Kopieren den Besitz stillschweigend übergibt. Der Nachfolger std::unique_ptr kann nicht kopiert werden, nur bewusst weitergegeben. std::shared_ptr zählt Referenzen und gibt das Objekt frei, wenn der letzte Besitzer weg ist:

#include <memory>

struct Konto { int stand; };

std::unique_ptr<Konto> a(new Konto);
a->stand = 100;
// std::unique_ptr<Konto> b = a;   // Fehler: nicht kopierbar

std::shared_ptr<Konto> s1(new Konto);
std::shared_ptr<Konto> s2 = s1;    // beide teilen sich das Objekt

Ein delete braucht man in beiden Fällen nicht mehr.

Move-Semantik und Threads

Move-Semantik (rvalue-Referenzen, &&) erlaubt es, den Inhalt eines temporären Objekts einfach zu übernehmen statt ihn zu kopieren. Das spart bei großen Containern und Strings viel Arbeit. Das Thema ist groß genug für einen eigenen Artikel, den ich später nachreiche. Auch std::thread ist jetzt Teil des Standards, ohne dass man auf plattformspezifische APIs ausweichen muss:

#include <iostream>
#include <thread>

int main()
{
    std::thread t([] { std::cout << "Hallo aus dem Thread" << std::endl; });
    t.join();
    return 0;
}

Mit GCC 4.6 übersetzt ihr das so: g++ -std=c++0x -pthread test.cpp.

Und die Compiler?

Der Stand ist noch uneinheitlich. GCC 4.6 beherrscht schon einiges, darunter auto, Lambdas, nullptr und die neue for-Schleife; den Schalter -std=c++0x braucht ihr dafür noch. Visual C++ 2010 kann auto, Lambdas, nullptr und rvalue-Referenzen, die Range-based for-Schleife und std::thread fehlen aber. Der bcc32 im C++Builder XE (der klassische Borland-Compiler) unterstützt von all dem nur sehr wenig. Wer wie ich unter dem C++Builder arbeitet, muss die neuen Sachen also vorerst in Projekten mit anderem Compiler ausprobieren. Für portablen Code heißt das: ausprobieren, was der eigene Compiler wirklich schluckt.

Nutzt ihr schon C++11-Features, und wenn ja, welche? Bis zum nächsten Mal!

Beitragsbild: eigene Collage mit Logos von Wikimedia Commons (ISO-C++-Logo: Jeremy Kratz, gemeinfrei); die Marken gehören ihren Inhabern.

$ whoami
Christian Ackermann

Softwareentwickler (C++, C, Java, C#) aus der Oberpfalz, bloggt seit 2001 über Linux, Technik und Kochen.