Betrachten Sie das folgende Beispiel:
#include <cstdlib>
int main() {
const int m = 42;
[] { m; }(); // OK
const int n = std::rand();
[] { n; }(); // error: 'n' is not captured
}
Warum muss ich n
im zweiten Lambda erfassen, aber nicht m
im ersten Lambda? Ich habe Abschnitt 5.1.2 ( Lambda-Ausdrücke ) im C ++ 14-Standard überprüft, aber keinen Grund gefunden. Können Sie mich auf einen Absatz verweisen, in dem dies erklärt wird?
Update: Ich habe dieses Verhalten sowohl bei GCC 6.3.1 als auch bei 7 (Trunk) beobachtet. Clang 4.0 und 5 (Trunk) schlagen in beiden Fällen mit einem Fehler fehl ( variable 'm' cannot be implicitly captured in a lambda with no capture-default specified
).
c++
lambda
constants
language-lawyer
s3rvac
quelle
quelle
constexpr
vsconst
m;
zum + 0;
std::array<int,m>
ist OK undstd::array<int,n>
nicht.constexpr
wenn Sie es nicht explizit angeben. Wenn jemand eineconstexpr
Variable will , sollte man sie als eine deklarieren.m
nichtconstexpr
, es ist ein konstanter integraler Ausdruck zur Kompilierungszeit ... das war eine Sache, lange bevor dasconstexpr
Schlüsselwort erfunden wurde.Antworten:
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
undn
in den obigen Beispielen ein.Die "bestimmten Kriterien" und "eingeschränkten Möglichkeiten" sind spezifisch (ab C ++ 14):
m;
ist einer davon) oderconst
, nichtvolatile
ganzzahlig oder enum, dessen Initialisierer ein konstanter Ausdruck war , oderconstexpr
, nichtvolatile
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. Dahern
kann nicht in einem Lambda verwendet werden, ohne gefangen genommen zu werden.m
wurde durch einen konstanten Ausdruck initialisiert42
, damit er die "bestimmten Kriterien" erfüllt. Ein Ausdruck mit verworfenem Wert verwendet den Ausdruck nicht oderm;
kann daher verwendet werden, ohnem
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 ziehtm
, gcc jedoch nicht. Der erste Satz von [basic.def.odr] / 3 ist ziemlich kompliziert: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.
quelle
m;
es irrelevant zu sein, ob eine Konvertierung von Wert zu Wert durchgeführt wird. Die Definition von odr-use in [basic.def.odr] / 3 besagt, dass "entweder die Umwandlung von lWert in rWert angewendete
wird odere
ein Ausdruck mit verworfenem Wert ist", und hier ist der Ausdruckm
ein Ausdruck mit verworfenem Wert.m
Am Ende wird die Variable also nicht odr-verwendet.Da es sich um einen konstanten Ausdruck handelt, behandelt der Compiler ihn so, als ob er es wäre
[] { 42; }();
Die Regel in [ expr.prim.lambda ] lautet:
Hier ein Zitat aus dem Standard [ basic.def.odr ]:
(Nicht so wichtiger Teil entfernt, um es kurz zu halten)
Mein einfaches Verständnis ist: Der Compiler weiß, dass dies
m
zur Kompilierungszeit konstant ist, währendn
es sich zur Laufzeit ändert und dahern
erfasst werden muss.n
wäre odr-verwendet, weil man sich zurn
Laufzeit tatsächlich ansehen muss, was drin ist . Mit anderen Worten ist die Tatsachen
relevant , dass "es nur eine geben kann" .Dies ist aus einem Kommentar von MM:
Hier finden Sie eine Demo .
quelle
n;
odr-verwendetn
, aberm;
nicht odr-verwendetm
n
aber nichtm
.EDIT: Die vorherige Version meiner Antwort war falsch. Anfänger ist richtig, hier ist relevantes Standardzitat:
[basic.def.odr]
Schon seit
m
es sich um einen konstanten Ausdruck handelt, wird er nicht von odr verwendet und muss daher nicht erfasst werden.Es scheint, dass das Clangs-Verhalten nicht dem Standard entspricht.
quelle