wake-up-neo.com

Ein positives Lambda: '+ [] {}' - Was für eine Zauberei ist das?

In Stack Overflow question Definieren von Lambdas in C++ 11 nicht zulässig, warum? wurde ein kleines Programm angegeben, das dies nicht tut kompilieren:

int main() {
    auto test = []{};
    test = []{};
}

Die Frage wurde beantwortet und alles schien in Ordnung zu sein. Dann kam Johannes Schaub und machte eine interessante Beobachtung :

Wenn Sie ein + Vor dem ersten Lambda beginnt es auf magische Weise zu arbeiten.

Also bin ich gespannt: Warum funktioniert folgendes?

int main() {
    auto test = +[]{}; // Note the unary operator + before the lambda
    test = []{};
}

Es kann problemlos mit GCC 4.7+ und Clang 3.2+ kompiliert werden. Entspricht der Codestandard?

254
Daniel Frey

Ja, der Code entspricht dem Standard. Das + Löst eine Konvertierung in einen einfachen alten Funktionszeiger für das Lambda aus.

Was passiert ist folgendes:

Der Compiler sieht das erste Lambda ([]{}) Und generiert ein Closure-Objekt gemäß §5.1.2. Da das Lambda ein nicht erfassendes Lambda ist, gilt Folgendes:

5.1.2 Lambda-Ausdrücke [expr.prim.lambda]

6 Der Verschlusstyp für einen Lambda-Ausdruck ohne Lambda-Erfassung hat eine öffentliche, nicht virtuelle, nicht explizite Konvertierungsfunktion für konstante Werte in einen Zeiger auf eine Funktion mit denselben Parametern und Rückgabetypen wie der Funktionsaufrufoperator des Abschlusstyps. Der von dieser Konvertierungsfunktion zurückgegebene Wert muss die Adresse einer Funktion sein, die beim Aufrufen dieselbe Wirkung hat wie beim Aufrufen des Funktionsaufrufoperators des Abschlusstyps.

Dies ist wichtig, da der unäre Operator + Eine Reihe von eingebauten Überladungen hat, insbesondere diese:

13.6 Eingebaute Operatoren

8 Für jeden Typ T existieren mögliche Operatorfunktionen des Formulars

T* operator+(T*);

Und damit ist klar, was passiert: Wenn der Operator + Auf das Abschlussobjekt angewendet wird, enthält die Menge der überladenen integrierten Kandidaten eine Konvertierung in einen beliebigen Zeiger, und der Abschluss-Typ enthält genau einen Kandidaten : Die Umwandlung in den Funktionszeiger des Lambda.

Der Typ von test in auto test = +[]{}; Wird daher zu void(*)() hergeleitet. Jetzt ist die zweite Zeile einfach: Für das zweite Lambda/Closure-Objekt löst eine Zuordnung zum Funktionszeiger die gleiche Konvertierung aus wie in der ersten Zeile. Obwohl das zweite Lambda einen anderen Verschlusstyp hat, ist der resultierende Funktionszeiger natürlich kompatibel und kann zugewiesen werden.

234
Daniel Frey