Für ein Lambda bei Block Umfang, Variablen bestimmte Kriterien erfüllen , in dem Erreichen Umfang kann in begrenzten Möglichkeiten innerhalb des Lambdas verwendet werden, auch wenn sie nicht erfaßt werden.
Grob gesagt umfasst das Erreichen des Gültigkeitsbereichs jede Variable, die lokal für die Funktion ist, die das Lambda enthält, und die sich zum Zeitpunkt der Definition des Lambdas im Gültigkeitsbereich befindet. Dies schließt also m
und n
in den obigen Beispielen ein.
Die "bestimmten Kriterien" und "eingeschränkten Möglichkeiten" sind spezifisch (ab C ++ 14):
- Innerhalb des Lambda darf die Variable nicht odr-verwendet werden , was bedeutet, dass sie keiner Operation unterzogen werden darf, außer:
- als Ausdruck für verworfene Werte erscheinen (
m;
ist einer davon) oder
- seinen Wert abrufen lassen.
- Die Variable muss entweder sein:
- A
const
, nicht volatile
ganzzahlig oder enum, dessen Initialisierer ein konstanter Ausdruck war , oder
- A
constexpr
, nicht volatile
variabel (oder ein Unterobjekt davon)
Verweise auf C ++ 14: [expr.const] /2.7, [basic.def.odr] / 3 (erster Satz), [expr.prim.lambda] / 12, [expr.prim.lambda] / 10.
Der Grund für diese Regeln ist, wie in anderen Kommentaren / Antworten vorgeschlagen, dass der Compiler in der Lage sein muss, ein Lambda ohne Erfassung als freie Funktion unabhängig vom Block zu "synthetisieren" (da solche Dinge in einen Zeiger konvertiert werden können). Funktionieren); Dies kann trotz Verweis auf die Variable erfolgen, wenn bekannt ist, dass die Variable immer denselben Wert haben würde, oder es kann die Prozedur zum Abrufen des Variablenwerts unabhängig vom Kontext wiederholen. Dies ist jedoch nicht möglich, wenn sich die Variable von Zeit zu Zeit unterscheiden kann oder wenn beispielsweise die Adresse der Variablen benötigt wird.
Wurde in Ihrem Code n
durch einen nicht konstanten Ausdruck initialisiert. Daher n
kann nicht in einem Lambda verwendet werden, ohne gefangen genommen zu werden.
m
wurde durch einen konstanten Ausdruck initialisiert 42
, damit er die "bestimmten Kriterien" erfüllt. Ein Ausdruck mit verworfenem Wert verwendet den Ausdruck nicht oder m;
kann daher verwendet werden, ohne m
erfasst zu werden. gcc ist richtig.
Ich würde sagen, dass der Unterschied zwischen den beiden Compilern darin besteht, dass clang die Verwendung m;
von odr in Betracht zieht m
, gcc jedoch nicht. Der erste Satz von [basic.def.odr] / 3 ist ziemlich kompliziert:
Eine Variable , x
deren Name als potentiell werteten Ausdrucks ex
ist odr verwendet durch , ex
es sei denn , die L - Wert-zu-R - Wert Umwandlung in Anwendung x
ergibt einen konstanten Ausdruck, der nicht invoke Jede nicht-triviale Funktionen bietet und, wenn x
ein Objekt ist, ex
ist ein Element von Die Menge der möglichen Ergebnisse eines Ausdrucks e
, bei dem entweder die Umwandlung von lWert in rWert angewendet e
wird oder e
ein Ausdruck mit verworfenem Wert ist.
Beim genauen Lesen wird jedoch ausdrücklich erwähnt, dass ein Ausdruck mit verworfenem Wert den Ausdruck nicht verwendet .
Die C ++ 11-Version von [basic.def.odr] enthielt ursprünglich nicht den Ausdruck für verworfene Werte, sodass das Verhalten von clang unter dem veröffentlichten C ++ 11 korrekt wäre. Der in C ++ 14 angezeigte Text wurde jedoch als Fehler in C ++ 11 ( Problem 712 ) akzeptiert , sodass Compiler ihr Verhalten auch im C ++ 11-Modus aktualisieren sollten.
constexpr
vsconst