Rozumiem, że prawidłowy sposób przechwytywania this
(modyfikowania właściwości obiektu) w lambdzie jest następujący:
auto f = [this] () { /* ... */ };
Ale jestem ciekawy następującej osobliwości, którą zauważyłem:
class C {
public:
void foo() {
// auto f = [] () { // this not captured
auto f = [&] () { // why does this work?
// auto f = [&this] () { // Expected ',' before 'this'
// auto f = [this] () { // works as expected
x = 5;
};
f();
}
private:
int x;
};
Dziwność, przez którą jestem zdezorientowany (i chciałbym uzyskać odpowiedź), jest powodem, dla którego działa:
auto f = [&] () { /* ... */ }; // capture everything by reference
I dlaczego nie mogę wyraźnie uchwycić this
przez odniesienie:
auto f = [&this] () { /* ... */ }; // a compiler error as seen above.
this
nie może być zmieniony, nie jest wystarczająco duży, aby odniesienie szybciej ... i mimo to , że w rzeczywistości nie istnieje , a więc ma żadne prawdziwe życie, co oznacza, że jakiekolwiek odniesienie do niego byłoby z definicji zwisające.this
jest prwartością, a nie lwartością.Odpowiedzi:
Przyczyna
[&this]
nie działa, ponieważ jest to błąd składniowy. Każdy parametr oddzielony przecinkami w tagulambda-introducer
tocapture
:capture: identifier & identifier this
Jak widać,
&this
nie jest to dozwolone składniowo. Powodem, dla którego nie jest to dozwolone, jest to, że nigdy nie chciałbyś przechwytywaćthis
przez odniesienie, ponieważ jest to mały wskaźnik do stałej. Zawsze chciałbyś przekazać to tylko według wartości - więc język po prostu nie obsługuje przechwytywaniathis
przez odniesienie.Aby przechwycić
this
jawnie, możesz użyć[this]
jakolambda-introducer
.Pierwszym
capture
może być a,capture-default
który jest:capture-default: & =
Oznacza to automatyczne przechwytywanie wszystkiego, czego używam, odpowiednio przez odniesienie (
&
) lub wartość (=
) - jednak traktowaniethis
jest specjalne - w obu przypadkach jest ujmowane według wartości z powodów podanych wcześniej (nawet przy domyślnym przechwytywaniu&
, co zwykle oznacza przechwytywanie przez odniesienie).5.1.2.7/8:
Zatem lambda zachowuje się tak, jakby była częścią otaczającej funkcji składowej, gdy używa się nazw składowych (tak jak w twoim przykładzie użycie nazwy
x
), więc wygeneruje „niejawne zastosowania”,this
tak jak robi to funkcja składowa.Więc można użyć
[this]
,[&]
,[=]
lub[&,this]
jakolambda-introducer
uchwycićthis
wskaźnik przez wartość.Jednak
[&this]
i[=, this]
są źle ukształtowane. W tym ostatnim przypadku gcc forgivingly dla ostrzega[=,this]
, żeexplicit by-copy capture of ‘this’ redundant with by-copy capture default
zamiast błędów.źródło
[&]
jeśli robisz coś takiego jak tworzenie bloku do przekazania do struktury kontrolnej", ale jawnie przechwytuj, jeśli tworzysz lambdę, która będzie używana do mniej prostych celów.[&]
to okropny pomysł, jeśli lambda ma przetrwać obecny zakres. Jednak wiele zastosowań lambd to tylko sposoby przekazywania bloków do kontrolowania struktur, a blok nie będzie trwał dłużej niż blok, w którym został utworzony.this
to słowo kluczowe, athis
nie identyfikator.Ponieważ standard nie ma
&this
na listach przechwytywania:N4713 8.4.5.2 Przechwytuje:
lambda-capture: capture-default capture-list capture-default, capture-list capture-default: & = capture-list: capture...opt capture-list, capture...opt capture: simple-capture init-capture simple-capture: identifier &identifier this * this init-capture: identifier initializer &identifier initializer
Tak, standardowe gwarancje
this
i*this
jest ważny, a&this
jest nieprawidłowy. Ponadto przechwytywaniethis
oznacza przechwytywanie*this
(co jest wartością l, samego obiektu) przez odniesienie , a nie przechwytywaniethis
wskaźnika według wartości !źródło
*this
przechwytuje obiekt według wartości