Z wyjątkiem banalnego przypadku, w którym jawnie przechwytujesz coś, co nie jest wymienione w lambda, istnieją dwa przypadki narożne, w których może występować różnica.
Po pierwsze, niejawne przechwytywania generalnie nie przechwytują elementów, które nie są odr-używane (ale zobacz następny element wyjątku do tego). Obejmuje to, między innymi, rzeczy, o których mowa w unevaluated argumentów, takich jak te z decltype
i sizeof
, a także niektórych const
i constexpr
zmiennych lokalnych stosowane w niektórych kontekstach (skonsultować [basic.def.odr] do pełnego zestawu zasad):
void f(int){}
void f2(const int &) {}
void t() {
const int x = 8;
constexpr double y = 8;
const double z = 8;
auto g = []{ f(x); }; // OK
auto g1 = [=]{ f(x); }; // OK, does not capture x
// usually won't fire, though not guaranteed
static_assert(sizeof(g) == sizeof(g1), "!!");
auto g2 = []{ f(y); }; // OK
auto g3 = []{ f(z); }; // Error, must capture z
auto g4 = []{ f2(x); }; // Error, must capture x since it's odr-used
auto g5 = [=]{ f2(x); }; // OK, captures x
auto g6 = []{ f2(+x); }; // OK, doesn't odr-use x
auto g7 = []{ f2(y); }; // OK
}
Jeśli ręcznie napiszesz listę przechwytywania, możliwe, że zrobisz więcej, niż potrzebujesz technicznie, ponieważ reguły określające, co jest lub nie są używane, są dość skomplikowane.
Po drugie, niejawne przechwytywanie w ogólnych lambdach uchwyci rzeczy używane w wyrażeniu zależnym, nawet jeśli niekoniecznie są one odr używane, dla zdrowia psychicznego. Pożyczanie an example from the standard:
void f(int, const int (&)[2] = {}) { } // #1
void f(const int&, const int (&)[1]) { } // #2
void test() {
const int x = 17;
auto g2 = [=](auto a) {
int selector[sizeof(a) == 1 ? 1 : 2]{};
f(x, selector); // OK: is a dependent expression, so captures x
// Will actually odr-use x only if sizeof(a) == 1
};
}
Jednak w rzeczywistości nie są wymagane, aby uchwycić coś, co może albo nie-może-być-odr wykorzystywane podczas pisania rodzajowe lambda; musisz go przechwycić tylko wtedy, gdy stworzysz specjalizację operatora wywołania funkcji, który używa odr. Jeśli więc nigdy nie wywołasz wynikowej ogólnej lambdy w sposób, który mógłby odr-użyć przedmiotowego problemu, to ukryte przechwytywanie może uchwycić więcej niż to, czego potrzebujesz. Na przykład, jest to dozwolone:
void test() {
const int x = 17;
auto g3 = [](auto a) { // no capture
int selector[sizeof(a) == 1 ? 1 : 2]{};
f(x, selector); // OK for now
};
}
dopóki nie nazywamy g3
z niczego, którego rozmiar wynosi 1: g3(0)
jest OK w typowych systemach; g3('\0')
nie jest.
Jeśli leniwy lambda kończy się przechwytywaniem tych samych zmiennych co pedantyczna lambda, nie widzę powodu, dla którego wydajność, a nawet generowane kody maszynowe powinny się różnić. – alain
IDK, jeśli jest dozwolony zgodnie ze standardem lub nie, ale myślę, że kompilator przechwyciłby tylko zmienne, których używasz. – NathanOliver
Różnica jest czysto syntaktyczna, a nie semantyczna. Obie lambdy robią to samo. –