Czy lambdy c ++ 11 przechwytują zmienne, których nie używają?

124

Kiedy używam [=] aby wskazać, że chciałbym, aby wszystkie zmienne lokalne były przechwytywane przez wartość w lambdzie, czy spowoduje to wszystkie zmienne lokalne w kopiowanej funkcji, czy tylko wszystkie zmienne lokalne, które są używane przez lambdę ?

Na przykład, jeśli mam:

vector<int> my_huge_vector(100000);
int my_measly_int;
some_function([=](int i){ return my_measly_int + i; });

Czy my_huge_vector zostanie skopiowany, mimo że nie używam go w lambdzie?

HighCommander4
źródło

Odpowiedzi:

115

Przechwytywana jest każda zmienna wyraźnie wymieniona na liście przechwytywania. Domyślne przechwytywanie przechwytuje tylko zmienne, które (a) nie są wyraźnie nazwane na liście przechwytywania i (b) są używane w treści wyrażenia lambda. Jeśli zmienna nie jest wyraźnie nazwana i nie używasz tej zmiennej w wyrażeniu lambda, to zmienna nie jest przechwytywana. W twoim przykładzie my_huge_vectornie jest schwytany.

Per C ++ 11 §5.1.2 [wyr.prim.lambda] / 11:

Jeśli wyrażenie lambda ma skojarzone z nim ustawienie domyślne przechwytywania i jego instrukcja złożona odr-uses this lub zmienna z automatycznym czasem przechowywania, a jednostka używana przez ODR nie jest jawnie przechwytywana, wówczas jednostka używana przez ODR jest przechwytywana niejawnie.

Twoje wyrażenie lambda ma skojarzone domyślne przechwytywanie: domyślnie przechwytujesz zmienne według wartości przy użyciu [=].

Jeśli i tylko wtedy, gdy używana jest zmienna (w rozumieniu reguły jednej definicji terminu „używana”), jest to zmienna przechwycona niejawnie. Ponieważ w ogóle nie używasz my_huge_vectorw treści („instrukcji złożonej”) wyrażenia lambda, nie jest ono przechwytywane niejawnie.

Aby przejść do §5.1.2 / 14

Jednostka jest przechwytywana przez kopię, jeśli

  • jest przechwytywany niejawnie, a domyślną wartością przechwytywania jest =lub jeśli
  • jest wyraźnie przechwycony za pomocą przechwycenia, które nie zawiera &.

Ponieważ twój my_huge_vectornie jest przechwytywany niejawnie i nie jest przechwytywany jawnie, nie jest w ogóle przechwytywany przez kopię ani przez odniesienie.

James McNellis
źródło
10
Czy masz święty cytat?
GManNickG
Powiem jednak, że całość § 5.1.2 jest ważna, aby zrozumieć wszystkie szczegóły. W tej sekcji zdefiniowano wiele terminów technicznych, a ponieważ definicje różnych składników wyrażeń lambda są z konieczności splątane, trudno jest wyciągnąć krótkie cudzysłowy, które ostatecznie mówią „to jest X i dlatego X”.
James McNellis
Pingowanie tutaj , aby zwrócić Twoją uwagę , co oznacza, że ​​taka optymalizacja jest niedozwolona, ​​przynajmniej w przypadku zmiennych nazwanych wprost. Nie jestem pewien, gdzie narysować granicę.
GManNickG
@GManNickG: To naprawdę niezły trolling ;-). Zajęło mi dobre trzy kliknięcia tego linku, zanim zdałem sobie sprawę, że faktycznie wskazuje na tę stronę ...: -O [W każdym razie ponownie przeczytam specyfikację językową, kiedy jutro rano wejdę do biura i zaktualizuję odpowiedź odpowiednio.]
James McNellis
O cholera, przepraszam !!! Odpowiedziano na moje pytanie, zamiast tego chciałem zamieścić tutaj link . To musiało być strasznie zagmatwane.
GManNickG
16

Nie, my_huge_vectornie zostanie schwytany. [=]oznacza, że ​​wszystkie używane zmienne są przechwytywane w lambdzie.

Thomas Minor
źródło
6
Tak. Należy jednak pamiętać, że użyte jest słowem technicznym i tak naprawdę oznacza użytą regułę jednej definicji . Rozważmy więc na przykład void f() { const int size(10); [] { int x[size]; }; }. Tutaj sizenie jest przechwytywany, ale jest w porządku, ponieważ nie jest używany w sensie ODR. (Visual C ++ 2010 nie akceptuje tego kodu, albo z powodu zmiany specyfikacji po wydaniu VC10, albo z powodu błędu, prawdopodobnie zostanie to naprawione w przyszłej wersji; g ++ 4.5.1 akceptuje go.)
James McNellis
@JamesMcNellis dp nie martw się, MSVC to dziś wciąż kupa śmierdzących bzdur. por. godbolt.org/z/vHnnCX (sprawdź w gcc dla lulz). To mówi; Nie rozumiem, dlaczego jakikolwiek identyfikator pojawiający się w ocenianym wyrażeniu nie miałby być używany przez ODR. Myślę, że ten przypadek jest zdecydowanie używany przez ODR, chyba że masz na myśli, że można go zinterpretować jako constexpr, więc tylko wartość jest przydatna? Nie jestem pewien, czy kompilator zakłada const, że rzeczy nie ulegają mutacji. chyba że super agresywna flaga optymalizacji OX lub coś takiego.
v.oddou