Ein positives Lambda: ‘+[]{}’ – Was ist das für eine Zauberei? [duplicate]

Lesezeit: 4 Minuten

Ein positives Lambda Was ist das fur eine
Daniel Frey

In Stack Overflow-Frage Umdefinieren von Lambdas in C++11 nicht erlaubt, warum?wurde ein kleines Programm angegeben, das nicht kompiliert:

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 setzen + Vor dem ersten Lambda fängt es auf magische Weise an zu arbeiten.

Da bin ich neugierig: Warum funktioniert folgendes?

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

Es kompiliert gut mit beiden GCC 4.7+ und Klirren 3.2+. Ist der Code standardkonform?

  • Es ist interessant, dass es für ein erfassendes Lambda nicht funktionieren würde.

    – Matthias M.

    19. September 2013 um 7:56 Uhr

  • @MatthieuM. Weil erfassende Lambdas nicht zu Funktionszeigern zerfallen! ;)

    – Markus García

    19. September 2013 um 7:57 Uhr

  • Andere + Quellenangaben folgen. Versuchen Sie dies auf GCC: struct foo { static const int n = 100; }; int main() { return std::max(0, +foo::n); }. Wenn Sie die entfernen + es kann nicht verlinkt werden, was ein standardkonformes Verhalten ist. VS2010 hat keine Probleme beim Verknüpfen (auch ohne die +).

    – Cassio Neri

    19. September 2013 um 9:02 Uhr

  • Zumindest verwandt: Auflösen mehrdeutiger Überladung auf Funktionszeiger und std::function für ein Lambda mit +

    – dyp

    19. September 2013 um 9:02 Uhr

  • Lassen Sie uns etwas mehr Magie hinzufügen: auto test = *[]{}; (Hinweis x ist hier noch ein Funktionszeiger, denke ich wegen Verfall) und dann.. auto test = +*[]{};. Das kannst du natürlich unendlich wiederholen: auto test = *+*+*+[]{};. Und mein Favorit: auto test = +*??(:>()<%??>;

    – dyp

    19. September 2013 um 9:31 Uhr


Ein positives Lambda Was ist das fur eine
Daniel Frey

Ja, der Code ist standardkonform. Die + 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 erzeugt ein Abschlussobjekt gemäß §5.1.2. Da das Lambda a ist nicht erfassen Lambda gilt:

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

6 Der Abschlusstyp für a Lambda-Ausdruck ohne Lambda-Erfassung verfügt über eine öffentliche, nicht virtuelle, nicht explizite Konstanten-Konvertierungsfunktion in einen Zeiger auf eine Funktion mit denselben Parameter- und Rückgabetypen wie der Funktionsaufrufoperator des Closure-Typs. Der von dieser Konvertierungsfunktion zurückgegebene Wert muss die Adresse einer Funktion sein, die, wenn sie aufgerufen wird, dieselbe Wirkung hat wie das Aufrufen des Funktionsaufrufoperators des Closure-Typs.

Dies ist als unärer Operator wichtig + hat eine Reihe von eingebauten Überladungen, insbesondere diese:

13.6 Eingebaute Operatoren [over.built]

8 Für jeden Typ T Es gibt Kandidatenoperatorfunktionen der Form

T* operator+(T*);

Und damit ist ganz klar, was passiert: Beim Operator + auf das Closure-Objekt angewendet wird, enthält die Menge der überladenen eingebauten Kandidaten einen Conversion-to-any-Pointer und der Closure-Typ enthält genau einen Kandidaten: Die Conversion zum Funktionszeiger des Lambda.

Die Art von test in auto test = +[]{}; ist daher abzuleiten void(*)(). Nun ist die zweite Zeile einfach: Beim zweiten Lambda/Closure-Objekt löst eine Zuweisung an den Funktionszeiger die gleiche Konvertierung aus wie in der ersten Zeile. Obwohl das zweite Lambda einen anderen Abschlusstyp hat, ist der resultierende Funktionszeiger natürlich kompatibel und kann zugewiesen werden.

  • Faszinierend. Und was ist der Sinn von unär + für Zeiger? Ich verstehe, dass es für numerische Typen zur Vollständigkeit mit unären existiert -. Aber unär - mit Zeigern macht keinen Sinn.

    – Tadeusz Kopec für die Ukraine

    19. September 2013 um 8:14 Uhr

  • Es ist nützlich, wenn Sie den Zerfall von Arrays oder Funktionen erzwingen möchten und wenn Sie die Heraufstufung von kleinen Integer-Typen oder Aufzählungen ohne Bereich erzwingen möchten.

    – Johannes Schaub – litb

    19. September 2013 um 9:17 Uhr

  • @TadeuszKopec … Und für alle Typen wird die Lvalue-ness entfernt. Sie können es verwenden, um einen Wert anstelle einer Referenz auf eine Funktion zu übergeben, die auf beiden überladen ist, z. B. durch perfekte Weiterleitung.

    – Kartoffelklatsche

    19. September 2013 um 10:04 Uhr

  • @Kartoffelwatter: “Und für alle Arten entfernt es den Wert.” +lvalue ist nicht für alle Typen definiert, und für einige von denen, bei denen es sich nur um einen überladenen Funktionsaufruf handelt, kann er jede Wertkategorie eines nicht verwandten Typs erzeugen, für einige andere Fälle erzeugt er nicht denselben Typ. Es wäre eine seltsame Verwendung, es zu verwenden, um einen Wert anstelle einer Referenz zu übergeben. Der bessere Weg wäre mit static_cast<T>(lvalue) um einen Prvalue vom Typ T aus einem Lvalue zu erhalten. “üblich, eine generische Funktion namens val() zu verwenden”: Das ist nicht klar, können Sie ein Beispiel / einen Link dafür zeigen val() Funktion?

    – Andreas Tomazos

    19. September 2013 um 11:04 Uhr

  • @ user1131467 Ich meine einfach, wenn Sie nicht speziell das integrierte verwenden operator+sollten Sie eine Funktion definieren val was effektiv tut die static_cast Sie erwähnen, anstatt sich an das Präfix zu halten + Notation.

    – Kartoffelklatsche

    19. September 2013 um 15:13 Uhr

994090cookie-checkEin positives Lambda: ‘+[]{}’ – Was ist das für eine Zauberei? [duplicate]

This website is using cookies to improve the user-friendliness. You agree by using the website further.

Privacy policy