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
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.
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.
.
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