Was lange währt, wird endlich wahr

Modernes C++11 (Teil 3)
Kommentare

Windows Developer

Der Artikel „Was lange währt, wird endlich wahr“ von Thomas Trotzki und Christian Binder ist erstmalig erschienen im Windows Developer 9.2012
Das in Listing 2 dargestellte lambda3

Windows Developer

Der Artikel „Was lange währt, wird endlich wahr“ von Thomas Trotzki und Christian Binder ist erstmalig erschienen im Windows Developer 9.2012

Das in Listing 2 dargestellte lambda3 zeigt die Verwendung der Capture Clause. Im Rumpf des Lambdas wird auf die Variable strMessage zugegriffen, die weder lokal noch als formaler Parameter definiert wurde. strMessage kommt aus dem Gültigkeitsbereich der umgebenden Funktion, im konkreten Fall ist dies DemoLambda. Somit erlaubt die Capture Clause gezielt den Gültigkeitsbereich des Lambdarumpfes um einzelne Variablen zu erweitern. Im Folgenden sehen Sie eine Übersicht der Capture Clause:

  • [] – Bleibt das Capture Clause leer, so können keine Variablen aus dem umgebenden Scope genutzt werden.
  • [=] – Es werden alle Variablen des umgebenden Gültigkeitsbereiches im Lambda gültig. Sie werden bei Aufruf implizit als weitere Parameter by Value übergeben.
  • [&] – Es werden alle Variablen des umgebenden Gültigkeitsbereiches im Lambda gültig, die Übergabe erfolgt nun aber by Reference.
  • [strMessage] – Es wird ausschließlich die angegebene Variable strMessage by Value übergeben.
  • [&strMessage] – Es wird ausschließlich die angegebene Variable strMessage by Reference übergeben.

Die aufgeführten Capture Clauses lassen sich weiter kombinieren. Sollen mehrere, aber nicht alle Variablen gezielt weitergereicht werden, so werden diese einfach innerhalb der eckigen Klammern kommagetrennt aufgelistet. Auch Kombinationen wie [=, &strMessage] sind möglich – hier werden alle Variablen by Value zugänglich, strMessage aber wird by Reference übergeben.

Warum aber werden die über die Capture Clause festgelegten Variablen nicht einfach als weitere Variablen in der Parameterliste hinzugefügt? Nach ein wenig Überlegung kann die Antwort selbst erkannt werden: Es geht um den Zeitpunkt der Parameterübergabe. Oder anders ausgedrückt: Wer legt die aktuellen Parameter fest? Bei der herkömmlichen Parameterliste ist dies der Rückrufer, bei der Capture Clause ist es der Rückgerufene. Die Werte der Caputure-Clause-Variablen werden zum Zeitpunkt der Definition des Lambdas gebunden, bei der herkömmlichen Parameterliste geschieht dies zum Zeitpunkt des Rückrufs.

Neue Containerklassen in der STL

Dass es in der STL auch eine Reihe an Containerklassen gibt, versteht sich von selbst. Neu ist mit C++11 nun aber die Nutzung der eben vorgestellten Lambdasyntax. Die STL macht hier ausgiebig Gebrauch davon. Listing 3 zeigt ein Beispiel, das die Verwendung des STL templates vector zeigt. Besonders dabei ist die Verwendung von der for_each loop. Diese iteriert über alle Elemente des Vektors und führt für jedes das angegebene Lambda aus. So kann recht einfach, wie mit print, ein Lambda definiert werden, das dann in mehreren for_each-Schleifen zum Einsatz kommt, eine lokale Funktion eben. Der Aufruf des Lambdas geschieht dabei innerhalb von for_each, d. h. innerhalb der STL. Denn: for_each ist kein Schlüsselwort, sondern eine Template-Funktion, die von der STL gestellt wird.

Listing 3: Lambdas und for_each

void DemoSort()
{
  vector v;
  for(int i=0; i<10; i++)
  {
    int val = rand()%100+1;
    v.push_back(val);
  }

  auto print = [] (int &val) 
  { cout << val << endl; };
  for_each(begin(v), end(v), print);
  cout << endl;

  auto comp = [] (int lhs, int rhs)
  { return lhs result;
  transform(begin(v), end(v), 
    inserter(result, result.end()), 
    [] (int val) -> int 
    { return (val>=50 && val <=70) ? val : 0; } );
  for_each(begin(result), end(result), print);
  cout << endl;

  result.clear();
  copy_if(begin(v), end(v), 
    inserter(result, result.end()), 
    [] (int val) -> bool 
    { return (val>=50 && val <=70); } );
  for_each(begin(result), end(result), print);
  cout << endl;
}  

Listing 3 zeigt auch die Verwendung weiterer STL Templates: sort, transform und copy_if. Alle verwenden als Parameter ein Lambda. Mal ist es eine Vergleichsfunktion für die Sortierung, mal ein Konverter, der z. B. aus einem Vektor mit Strukturen ein konkretes Element jeder Strukturinstanz in einen neuen Vektor mit integer-Werten wandelt, oder eine Prüffunktion, die dafür sorgt, dass nur einzelne Elemente eines Vektors in einen anderen übernommen werden. Schöne neue Welt der Lambdas!

Recht interessant ist auch die Tatsache, dass die meisten Compilerhersteller bereits beginnen, diese neuen Leistungsmerkmale mit ihren teilweise proprietären Bibliotheken zu verheiraten. Microsoft zum Beispiel sorgt in Visual Studio 2012 dafür, dass die Containerklassen der STL sehr smooth in Containerklassen der WinRT [1] konvertieren und umgekehrt.

rvalue references

In C++99 gibt es keine Möglichkeit, beim Überladen von Funktionen mit einem Referenzparameter zu unterscheiden, ob dieser beim Aufruf mit einem rvalue oder einem lvalue belegt wurde:

int a=0;
void f(int &i); // lvalue reference
f(a); // OK, a ist lvalue
f(5); // error, 5 is rvalue  

Um den resultierenden Compilerfehler zu umgehen, behilft sich der C++-99-Programmierer mit dem Überladen der Funktion f():

void f(int &i); // lvalue reference
void f(const int &i); // const lvalue reference

f(5); // calls void f(const int &i)  

Doch dies ist nicht genau das, was gewünscht ist, denn nun wird der Parameter i innerhalb der Funktion f const und kann damit nicht mehr modifiziert werden. C++11 führt zur vollwertigen Unterscheidung von lvalue und rvalue reference eine neue Syntax ein:

void f(int &i); // lvalue reference
void f(const int &i); // const lvalue reference
void f(int &&i); // rvalue reference

f(5); // calls void f(int &&i)  
Unsere Redaktion empfiehlt:

Relevante Beiträge

Meinungen zu diesem Beitrag

X
- Gib Deinen Standort ein -
- or -