2012-07-10 13 views
6

Si consideri il seguente codice:L'acquisizione lambda causa errori di operando incompatibili?

main() 
{ 
    bool t; 
    ... 
    std::function<bool (bool)> f = t ? [](bool b) { return b; } : [](bool b) { return !b; }; // OK 
    std::function<bool (bool)> f = t ? [t](bool b) { return t == b; } : [t](bool b) { return t != b; }; // error 
} 

quando si compila con Clang 3.1, l'assegnazione di non-cattura lambda funziona, mentre quello con cattura fallisce:

main.cpp:12:36: error: incompatible operand types ('<lambda at main.cpp:12:38>' and '<lambda at main.cpp:12:71>') 
     std::function<bool (bool)> f2 = t ? [t](bool b) { return t == b; } : [t](bool b) { return t != b; }; // error 
             ^~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 

Perché catturare la stessa variabile fa sì che il 2 lambda essere di tipi incompatibili?

risposta

12

Il tipo di una lambda è "un tipo di classe univoco, non unione" chiamato tipo di chiusura. Ogni lambda è implementata come un tipo diverso, locale rispetto all'ambito della dichiarazione, che ha un operatore sovraccarico() per chiamare il corpo della funzione.

Esempio: se si scrive questo:

auto a=[t](bool b){return t==b;}; 
auto b=[t](bool b){return t!=b;}; 

Poi il compilatore compila questo (più o meno):

class unique_lambda_name_1 
{ 
bool t; 
public: 
unique_lambda_name_1(bool t_) t(_t) {} 
bool operator() (bool b) const { return t==b; } 
} a(t); 
class unique_lambda_name_2 
{ 
bool t; 
public: 
unique_lambda_name_2(bool t_) t(_t) {} 
bool operator() (bool b) const { return t!=b; } 
} b(t); 

A e B hanno tipologie diverse e non può essere utilizzato nell'operatore?:.

Tuttavia, §5.1.2 (6) afferma che il tipo di chiusura di un lambda senza acquisizione ha un operatore di conversione pubblico non esplicito, che converte il lambda in un puntatore di funzione - non è possibile implementare chiusure come semplici funzioni. Qualsiasi lambda con lo stesso argomento e tipi di ritorno può essere convertito nello stesso tipo di puntatore e quindi l'operatore ternario?: Può essere applicato a essi.

Esempio: lambda non cattura:

auto c=[](bool b){return b;}; 

è implementato in questo modo:

class unique_lambda_name_3 
{ 
static bool body(bool b) { return b; } 
public: 
bool operator() (bool b) const { return body(b); } 
operator decltype(&body)() const { return &body; } 
} c; 

che significa che questa linea:

auto x = t?[](bool b){return b;}:[](bool b){return !b;}; 

significa in realtà questo:

// a typedef to make this more readable 
typedef bool (*pfun_t)(bool); 
pfun_t x = t?((pfun_t)[](bool b){return b;}):(pfun_t)([](bool b){return !b;}); 
+0

Grazie per la spiegazione dettagliata. Non sapevo che fossero implementati in modo diverso. Ha senso ora. –

Problemi correlati