2014-04-06 9 views
7

Ho appena iniziato ad apprendere le nuove funzionalità in C++ 11. Stavo leggendo di lambda in C++ Primer (Stanley Lippman) e stavo sperimentando con loro.Espressioni lambda C++ - Come li interpreta il compilatore?

ho provato i seguenti pezzi di codice:

auto func() -> int (*)(){ 
    //int c=0; 
    return []()-> int {return 0;}; 
} 

int main(){ 
    auto p = func(); 
} 

Questo codice compilato bene. Quindi immagino che lambda senza catture vengano semplicemente generate come normali funzioni dal compilatore e possiamo usare un normale puntatore a loro funzione.

Ora ho cambiato il codice per utilizzare Cattura:

auto func() -> int (*)(){ 
    int c=0; 
    return [=]()-> int {return c;}; 
} 

int main(){ 
    auto p = func(); 
} 

Ma questo non è riuscito per la compilazione. Ho ottenuto il seguente errore di compilazione durante l'utilizzo di g ++:

main.cpp: In function ‘int (* func())()’: 
main.cpp:6:31: error: cannot convert ‘func()::__lambda0’ to ‘int (*)()’ in return 
return [=]()-> int {return c;}; 

Dal errore posso capire che non è una normale funzione che viene generato e potrebbe probabilmente essere una classe con una chiamata-operatore di overload. O è qualcos'altro?

Le mie domande: in che modo il compilatore gestisce internamente Lambdas? Come dovrei passare i lambda che usano le catture, quale dovrebbe essere il valore di ritorno da func()? Attualmente non posso pensare a un caso d'uso in cui avrei bisogno di usare lambda come questo, ma voglio solo capirne di più. Per favore aiuto.

Grazie.

+0

Try questo link: http://www.cprogramming.com/c++11/c++11-lambda-closures.html –

risposta

8

Tutti lambda sono oggetti con l'attuazione definiti tipo chiamato tipo di chiusura con un membro operator(). Ogni espressione lambda ha anche il proprio tipo di chiusura.

Lambdas senza acquisizione può essere convertito in un puntatore di funzione. Se il compilatore genera una normale funzione dietro le quinte è un dettaglio interno e non dovrebbe importarti.

Non è possibile restituire un lambda definito all'interno di una funzione. Ci sono poche cose che impediscono questo - non si conosce il nome di un tipo di espressione lambda, non si può usare un'espressione lambda all'interno di uno decltype e come già detto, due espressioni lambda (anche se lessicalmente identiche) hanno tipi diversi .

Che cosa si può fare è utilizzare std::function:

std::function<int()> func() 
{ 
    int i = 0; 
    return [=]()-> int {return i;}; 
} 

In questo modo funziona con cattura, anche.

O qualcosa di simile:

auto f = []{ return 0; }; 

auto func() -> decltype(f) 
{ 
    return f; 
} 

EDIT: Lo standard imminente C++ 1A (più precisamente, tipo di ritorno deduzione), invece, vi permetterà di fare questo:

auto func() 
{ 
    int i = 42; 
    return [=]{ return i; }; 
} 
+2

L'unica risposta per menzionare la conversione al puntatore di funzione! ?? +1 Nota che puoi restituire un lambda da un lambda, e in C++ 1y anche da una funzione, tramite la deduzione del tipo di ritorno. Il tipo di espressione lambda è * mai * un tipo di funzione per qualsiasi comportamento osservabile (ovviamente, è implementato tipicamente come funzione (membro)). Ad esempio, è garantito che un lambda non corrisponderà ad alcuni generici 'Ret (Params ...)' nella deduzione del tipo di modello. – dyp

+0

@dyp Giusto, grazie. Ho aggiornato con un esempio di deduzione di tipo restituito. – jrok

2

Il compilatore è libero di decidere come generare il tipo di una funzione lambda. Genera un oggetto funzione che significa che è possibile richiamarlo tramite (). Ma il tipo può essere diverso in varie situazioni. Ogni singola lambda può avere un tipo unico. Un semplice lambda (non cattura nulla) può essere memorizzato in un puntatore per funzionare.

In C++ 11, per avvolgere una lambda che cattura i dati, è possibile utilizzare std::function:

auto func() -> std::function<int()> { 
    int c = 0; 
    return [=]()-> int {return c;}; 
} 

 

In C++ 14, ha function return type deduction, lascia gestire la compilazione esso :

auto func() { 
    int c = 0; 
    return [=]()-> int {return c;}; 
} 
+0

I lambda non catturanti non possono essere memorizzati in un puntatore alla funzione: possono essere convertiti in un puntatore a funzione. – Yakk

+0

@Yakk: Puoi spiegare la differenza tra "essere memorizzati" e "essere convertiti"? Se vuoi dire che i lambda non catturanti verranno lanciati prima di essere memorizzati nel puntatore alle funzioni, allora ti sbagli. Lambdas non ha tipi specifici, chissà, forse il compilatore usa il puntatore al tipo di funzione per i lambda semplici e possono essere assegnati direttamente al puntatore alle funzioni. – deepmax

+0

Un lambda ** non ** corrisponde a un puntatore alla funzione 'template', garantito. Quindi no, non credo che tu abbia ragione. In pratica, ogni compilatore usa un tipo unico generato che rende banale la chiamata banale: un puntatore a funzione rende inlinimg ingannevole e fragile. Quindi in teoria, e in pratica, si tratta di una conversione in un tipo diverso con proprietà diverse. – Yakk

2

Una funzione non può restituire una funzione lambda. Se si scrive:

auto func() -> int() { 
    int c=0; 
    return [=]()-> int {return c;}; 
} 

Poi GCC si lamenta:

lambda.cpp:3:21: error: function return type cannot be function 
auto func() -> int(){ 

Quindi la soluzione è quella di avvolgere l'oggetto di ritorno in un std::function: funzione di

#include <functional> 
    auto func() -> std::function<int()> { 
    int c=0; 
    return [=]()-> int {return c;}; 
} 

int main(){ 
    auto p = func(); 
} 
Problemi correlati