Lambda-Erfassung und gleichnamiger Parameter – wer beschattet den anderen? (clang vs gcc)

Lesezeit: 5 Minuten

Lambda Erfassung und gleichnamiger Parameter wer beschattet den anderen clang
Vittorio Romeo

auto foo = "You're using g++!";
auto compiler_detector = [foo](auto foo) { std::puts(foo); };
compiler_detector("You're using clang++!");
  • kling++ 3.6.0 und neuerer Ausdruck “Du verwendest clang++!” und davor warnen zu erfassen foo unbenutzt sein.

  • g++ 4.9.0 und neuerer Ausdruck “Du verwendest g++!” und davor warnen Parameter foo unbenutzt sein.

Welcher Compiler folgt hier genauer dem C++-Standard?

Beispiel Zauberstab

  • Einfügen des Codes von Zauberstabbox zu Hier (Sie scheinen den Share-Button vergessen zu haben) lässt es so aussehen, als ob VS2015 (?) Mit dem Clang-Spruch übereinstimmt Warnung C4458: Deklaration von „foo“ verbirgt Klassenmitglied.

    – nwp

    7. Februar 17 um 11:13 Uhr


  • Tolles Beispiel..

    – Abweichler

    7. Februar 17 um 11:13 Uhr

  • Das Lambda hat einen Typ mit einem Vorlagenfunktionsaufrufoperator, daher würde mich die Logik dazu bringen, zu sagen, dass der Parameter die erfasste Variable schattieren sollte, als ob sie darin wäre struct Lambda { template<typename T> void operator()(T foo) const { /* ... */ } private: decltype(outer_foo) foo{outer_foo}; }.

    – Skypejack

    7. Februar 17 um 11:18 Uhr


  • @nwp VS ist falsch, Datenmitglieder des Lambda sind unbenannt und können daher nicht beschattet werden. Der Standard besagt, dass “der Zugriff auf eine erfasste Entität in den Zugriff auf das entsprechende Datenelement umgewandelt wird”, was uns auf den Punkt bringt.

    – n. 1.8e9-wo-ist-meine-Aktie m.

    7. Februar 17 um 11:33 Uhr


  • Ich würde hoffen, dass die Clang-Version korrekt ist – es würde neue Wege gehen, wenn etwas außerhalb einer Funktion den Funktionsparameter beschattet, anstatt umgekehrt!

    – MM

    7. Februar 17 um 13:08 Uhr

1642348688 690 Lambda Erfassung und gleichnamiger Parameter wer beschattet den anderen clang
Columbo

Update: Wie vom Core-Lehrstuhl im unteren Zitat versprochen, ist der Code jetzt schlecht geformt:

Wenn ein Kennung in einem einfach erfassen erscheint als die Deklarator-ID eines Parameters der Lambda-Deklarator‘S Parameterdeklarationsklausel, ist das Programm fehlerhaft.


Vor einiger Zeit gab es einige Probleme mit der Namenssuche in Lambdas. Sie wurden von gelöst N2927:

Der neue Wortlaut verlässt sich nicht mehr auf die Suche, um die Verwendung erfasster Entitäten neu zuzuordnen. Es bestreitet die Interpretationen deutlicher als die eines Lambdas Zusammengesetzte Aussage in zwei Durchgängen verarbeitet wird oder dass irgendwelche Namen darin enthalten sind Zusammengesetzte Aussage könnte in ein Mitglied des Closure-Typs aufgelöst werden.

Die Suche erfolgt immer im Kontext von Lambda-Ausdruck, niemals “nach” der Umwandlung in den Elementfunktionskörper eines Closure-Typs. Sehen [expr.prim.lambda]/8:

Der Lambda-Ausdruck‘S Zusammengesetzte Aussage ergibt die Funktionskörper ([dcl.fct.def]) des Funktionsaufrufoperators, aber zum Zwecke der Namenssuche, […], das Zusammengesetzte Aussage wird im Kontext betrachtet Lambda-Ausdruck.[[Beispiel:

struct S1 {
  int x, y;
  int operator()(int);
  void f() {
    [=]()->int {
      return operator()(this->x+y);  // equivalent to: S1::operator()(this->x+(*this).y)
                                     // and this has type S1*
    }; 
  }
};

Ende Beispiel ]

(Das Beispiel macht auch deutlich, dass die Suche das generierte Capture-Member des Closure-Typs nicht irgendwie berücksichtigt.)

Der Name foo wird bei der Erfassung nicht (erneut) deklariert; es wird in dem Block deklariert, der den Lambda-Ausdruck umschließt. Der Parameter foo wird in einem Block deklariert, der in diesem äußeren Block verschachtelt ist (siehe [basic.scope.block]/2, die auch explizit Lambda-Parameter erwähnt). Die Reihenfolge der Suche ist eindeutig von inneren zu äußeren Blöcken. Daher sollte der Parameter ausgewählt werden, dh Clang hat Recht.

Wenn Sie die Erfassung zu einer Init-Erfassung machen würden, dh foo = "" anstatt foo, wäre die Antwort nicht klar. Das liegt daran, dass das Capturen nun tatsächlich erfolgt veranlasst eine Erklärung dessen “Block” nicht gegeben ist. Ich habe den Kernvorsitzenden diesbezüglich angeschrieben, der geantwortet hat

Dies ist Ausgabe 2211 (eine Liste neuer Ausgaben wird in Kürze auf der Website open-std.org erscheinen, leider nur mit Platzhaltern für eine Reihe von Ausgaben, von denen dies eine ist; ich arbeite hart daran, diese Lücken vor dem Kona zu füllen Sitzung am Ende des Monats). CWG diskutierte dies während unserer Telefonkonferenz im Januar und Die Anweisung besteht darin, das Programm schlecht formatiert zu machen, wenn ein Erfassungsname auch ein Parametername ist.

  • Hier kann ich nichts auseinander reißen 🙂 A einfach erfassen deklariert nichts, daher ist das korrekte Ergebnis der Namenssuche ziemlich offensichtlich (Übrigens, GCC macht es richtig, wenn Sie a verwenden Capture-Standard statt expliziter Erfassung). init-captures sind etwas kniffliger.

    – TC

    7. Februar 17 um 14:13 Uhr

  • @TC Ich stimme zu. Ich habe ein Kernproblem eingereicht, aber anscheinend wurde dies bereits besprochen, siehe die bearbeitete Antwort.

    – Kolumbus

    8. Februar 17 um 2:04 Uhr

Lambda Erfassung und gleichnamiger Parameter wer beschattet den anderen clang
skyjack

Ich versuche, ein paar Kommentare zu der Frage zusammenzupacken, um Ihnen eine aussagekräftige Antwort zu geben.
Beachten Sie zunächst Folgendes:

  • Nicht statische Datenmember werden für das Lambda für jede kopiererfasste Variable deklariert
  • In diesem speziellen Fall hat das Lambda einen Closure-Typ, der einen öffentlichen Inline-Template-Funktionsaufrufoperator hat, der einen benannten Parameter akzeptiert foo

Daher würde mich die Logik auf den ersten Blick dazu bringen, zu sagen, dass der Parameter die erfasste Variable schattieren sollte, als ob in:

struct Lambda {
    template<typename T> void operator()(T foo) const { /* ... */ }
    private: decltype(outer_foo) foo{outer_foo};
};

Wie auch immer, @nm hat richtigerweise darauf hingewiesen, dass die nicht statischen Datenelemente, die für kopiererfasste Variablen deklariert sind, tatsächlich unbenannt sind. Davon abgesehen wird auf das unbenannte Datenelement immer noch mittels einer Kennung zugegriffen (d. h foo). Daher sollte der Parametername des Funktionsaufrufoperators immer noch (lassen Sie mich sagen) Schatten Sie diese Kennung.
Wie von @nm in den Kommentaren zur Frage richtig hervorgehoben:

die ursprünglich gefangene Entität […] sollten gemäß den Bereichsregeln normal schattiert werden

Aus diesem Grund würde ich sagen, dass Clang Recht hat.

  • Wie oben erläutert, wird die Suche in diesem Kontext niemals so durchgeführt, als ob wir uns im transformierten Closure-Typ befänden.

    – Kolumbus

    7. Februar 17 um 12:41 Uhr


  • @Columbo Ich füge eine Zeile hinzu, die ich verpasst habe, auch wenn es aus der Argumentation klar hervorging, dass Clang richtig ist. Der lustige Teil ist, dass ich fand [expr.prim.lambda]/8 beim Versuch, eine Antwort zu geben, aber ich konnte es nicht so richtig verwenden, wie Sie es getan haben. Deshalb ist es jedes Mal eine Freude, Ihre Antworten zu lesen. 😉

    – Skypejack

    7. Februar 17 um 12:58 Uhr

.

507460cookie-checkLambda-Erfassung und gleichnamiger Parameter – wer beschattet den anderen? (clang vs gcc)

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

Privacy policy