Escludendo il caso banale in cui si acquisisce esplicitamente qualcosa che non è menzionato nel lambda, ci sono due casi d'angolo in cui potrebbe esserci una differenza.
In primo luogo, le acquisizioni implicite in genere non acquisiscono entità che non sono utilizzate odr (ma vedere l'elemento successivo per l'eccezione a questo). Questo include, tra le altre cose, le cose di cui operandi non valutate come quelli di decltype
e sizeof
, così come alcune variabili locali const
e constexpr
se usato in alcuni contesti (consultare [basic.def.odr] per l'intero insieme di regole):
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
}
Se si scrive la lista di cattura manualmente, è possibile che catturerai più di quanto tecnicamente necessario, perché le regole che governano ciò che è o non è usato odr sono piuttosto complesse.
In secondo luogo, le acquisizioni implicite nei lambda generici cattureranno le cose utilizzate in un'espressione dipendente, anche quando non sono necessariamente odr-usate, per la sanità mentale. Prendendo in prestito 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
};
}
Tuttavia, non esiste alcun obbligo di catturare qualcosa che può-o-può-non-essere-ODR-utilizzato quando si scrive un lambda generica; ti viene richiesto di catturarlo solo se installi una specializzazione dell'operatore di chiamata di funzione che odr-usa la cosa. Pertanto, se non si chiama mai il lambda generico risultante in un modo che potrebbe utilizzare la cosa in questione, l'acquisizione implicita potrebbe catturare più del minimo necessario. Ad esempio, questo è consentito:
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
};
}
finché non si chiama g3
con qualsiasi cosa la cui dimensione è 1: g3(0)
è OK su sistemi tipici; g3('\0')
no.
Se il lambda pigro finisce per catturare le stesse variabili del lambda pedante, non vedo alcun motivo per cui le prestazioni o anche i codici macchina generati dovrebbero differire. – alain
IDK se consentito dallo standard o no, ma penserei che il compilatore catturerebbe solo le variabili che si usano. – NathanOliver
La differenza è puramente sintattica, non semantica. Entrambi i lambda fanno la stessa cosa. –