2013-07-15 32 views
16

mi piacerebbe fare qualcosa di simile:Passo lambda espressione all'argomento lambda C++ 11

int main() 
{ 
    auto f = [/*some variables*/](/*take lambda function*/) 
    {/*something with lambda function*/}; 

    f([/*other variables*/](/*variables to be decided by f()*/) 
    {/*something with variables*/}); 
} 

so che è possibile passare un lambda a una funzione, così come ad un lambda. le seguenti opere:

int main() 
{ 
    int x=0; 
    int y=0; 
    auto f = [x,y](double (func)(int)) -> double 
    {func(0); return 0.0;}; 

    f([](int i) -> double 
    {return 0.0;}); 
} 

Ma il seguente non funziona (non appena a cambiare le variabili di ambito per aggiungere [x])

int main() 
{ 
    int x=0; 
    int y=0; 
    auto f = [x,y](double (func)(int)) -> double 
    {func(0); return 0.0;} 

    f([x](int i) -> double //[x] does not work 
    {return 0.0;}); 
} 

che dà l'errore:

error: function "lambda [](double (*)(int))->double::operator()" cannot be called with the given argument list 
     argument types are: (lambda [](int)->double) 
     object type is: lambda [](double (*)(int))->double 

qualcuno dovrebbe avere un'idea su come risolvere questo problema o un modo per aggirarlo? Sto usando il compilatore intel ICPC (ICC) 13.1.2 con std = C++ 11

Grazie

+13

Catturare lambda non è convertibile in puntatori di funzione –

+0

perché non un 'constexpr'? 'auto' è una funzionalità in fase di compilazione comunque ... – user2485710

+4

@AndyProwl Cosa? Nessuna risposta dettagliata da te entro 10 minuti? Dai, ci fai lavorare troppo duramente qui, e non hai nemmeno una copia per il giorno ;-) – TemplateRex

risposta

30

Ci sono un paio di cose da chiarire riguardo alla tua domanda. Il primo dei quali è cos'è un lambda?

Un'espressione lambda è un'espressione semplice da cui il compilatore genererà un tipo univoco che non può essere denominato e allo stesso tempo genererà un'istanza del tipo. Quando si scrive: [](int i) { std::cout << i; } il compilatore genera un tipo per te, che è più o meno:

struct __lambda_unique_name { 
    void operator()(int i) const { std::cout << i; } 
}; 

Come si può vedere, è non una funzione, ma un tipo che implementa operator() in funzione const membro. Se lambda ha effettuato alcuna cattura, il compilatore genererebbe il codice per acquisire il valore/i riferimenti.

Come caso angolo, per lambda come sopra, dove non c'è stato essere catturato, il linguaggio consente una conversione dal tipo lambda a un puntatore a funzionare con la firma del operator() (meno la parte this) , in modo che il lambda sopra può essere implicitamente convertito in un puntatore a funzione di prendere int e rinviare nulla:

void (*f)(int) = [](int i) { std::cout << i; } 

Ora che le basi sono stati esposti, nel codice si dispone di questo lambda:

auto f = [x,y](double (func)(int)) -> double {func(0); return 0.0;}; 

Le regole per i parametri alle funzioni (che si applicano anche ai lambdas) determinano che un argomento non può essere di tipo funzione, quindi l'argomento del lambda decade a un puntatore alla funzione (nello stesso modo in cui un argomento di tipo array decade ad un tipo di puntatore):

auto f = [x,y](double (*func)(int)) -> double {func(0); return 0.0;}; 

in un momento successivo si sta tentando di passare un lambda che ha una cattura come argomento. Poiché esiste un'acquisizione, la regola speciale non si applica e la lambda non è convertibile in un puntatore alla funzione che genera l'errore del compilatore che vedi.

Nello standard corrente è possibile scegliere tra due metodi. È possibile utilizzare tipo-cancellazione per rimuovere l'esatto tipo di entità richiamabile dalla firma:

auto f = [x,y](std::function<double(int)> func) -> double {func(0); return 0.0;}; 

Perché un std::function<double(int)> può essere inizializzato con qualsiasi callable entità con l'indicatore appropriato, questo accetterà la lambda nella codice sottostante, al costo della cancellazione del tipo che di solito implica un'allocazione dinamica e un dispacciamento dinamico.

In alternativa, è possibile eliminare lo zucchero sintattico e arrotolare manualmente il primo equivalente lambda, ma renderlo generico. In questo caso, in cui il lambda è semplice questo potrebbe essere un'opzione valida:

struct mylambda { 
    template <typename F> 
    double operator()(F fn) const { 
     fn(0); return 0.0; 
    } 
} f; 
// then use the non-lambda as you tried: 
f([x](int i) -> double {return 0.0;}); 

Infine, se siete abbastanza pazienti, è possibile attendere per C++ 14, in cui (molto probabilmente, non è ancora stato ratificato) ci sarà il supporto per lambda polimorfici che semplificano la creazione della classe di cui sopra:

auto f = [](auto fn) { fn(0.0); return 0.0; } // unrolls to 'mylambda' above 
+1

Da quello che ho letto, e anche da [questo commento] (http://stackoverflow.com/questions/7951377/what-is-the-type-of-lambda-when-deduced-with-auto-in-c11#comment14872356_8050769) 'std :: function' è un meccanismo più lento rispetto alla tecnica del template tu mostri, anche se alcuni dicono che questo è dovuto ad una chiamata virtuale, mentre tu dici che è dovuto all'allocazione dinamica (anche se tu dici anche dispatch dinamico che presumo sia sinonimo di 'virtuale'). In generale, consiglieresti di usare modello? – johnbakers

5

Provare a usare std :: funzione:

#include <functional> 
int main() 
{ 
    int x=0; 
    int y=0; 
    auto f = [x,y](std::function<double(int)> func) -> double 
      {func(0); return 0.0;}; 

    f([x](int i) -> double {return 0.0;}); 
} 
+0

Grazie per una rapida risposta Ho provato a utilizzarlo, ma il compilatore Intel non lo supporta come mostrato [qui] (http://stackoverflow.com/questions/6527064/stdfunction-and-the-intel-compiler-version-11-1). La libreria boost/funzione potrebbe essere un'alternativa potenziale, ma vorrei provare a evitare di importarla.Quale potrebbe essere un'alternativa? – Couchy311

+1

Aggiorna/cambia il tuo compilatore. Non puoi semplicemente catturare le variabili in puntatori di funzioni raw, hai bisogno di un oggetto per questo. Se il compilatore fornisce lambda ma non c'è modo di memorizzarli (che è lo scopo principale di std :: function) IMHO non hai davvero il supporto per lambda nel tuo compilatore. – DanielKO

+0

hmmm ... vero, probabilmente dovrei trovare un modo per aggirare lambdas :( – Couchy311

1

Si potrebbe provare qualcosa di simile il seguente se si conosce il tipo di lambda in anticipo, per esempio:

int main() 
{ 
    int x = 0, y = 0; 

    auto f = [x](int i)->double { 
     return (double)x; 
    }; 

    auto f2 = [x,y](decltype(f) func)->double { 
     return func(0); 
    }; 

    f2(f); 

    return 0; 
} 

O alternativa si potrebbe utilizzare la libreria <functional> fo ra soluzione più generico, per esempio:

auto f = [x,y](std::function<double(int)> func) { /* Do stuff */ }; 
3

Si possono avere a mordere semplicemente il proiettile e implementare le proprie funtori come abbiamo fatto in tempi bui:

struct F { 
    int x; 
    int y; 

    F(int x_, int y_) : x(x_), y(y_) {} 

    template <typename G> 
    double operator() (G&& g) const { 
     g(0); 
     return 0.0; 
    } 
}; 

#include <iostream> 

int main() 
{ 
    int x = 0; 
    int y = 0; 
    auto f = F(x, y); 

    f([x](int i){return 0.0;}); 
    f([](int i){std::cout << i << std::endl;}); 
} 

Questo dovrebbe tenervi andando fino a quando il il compilatore supporta i lambda generici in C++ 14.

+0

Qual è lo scopo o il vantaggio dell'uso di un riferimento rvalue lì? (Es. Come '(G && g)' meglio di '(G g)' ? –

+1

@ChrisBecke L'accettazione di un parametro inoltrando il riferimento senza l'inoltro è unidirezionale per "Voglio modificare questo argomento ed essere ignaro di valutare la categoria." – Casey

0

È possibile cify un lambda cattura, ma questa soluzione ha i suoi limiti:

#include <new> 

#include <utility> 

namespace 
{ 

template <typename F, int I, typename L, typename R, typename ...A> 
inline F cify(L&& l, R (*)(A...) noexcept(noexcept(
    std::declval<F>()(std::declval<A>()...)))) 
{ 
    static L l_(std::forward<L>(l)); 
    static bool full; 

    if (full) 
    { 
    l_.~L(); 

    new (static_cast<void*>(&l_)) L(std::forward<L>(l)); 
    } 
    else 
    { 
    full = true; 
    } 

    return [](A... args) noexcept(noexcept(
     std::declval<F>()(std::forward<A>(args)...))) -> R 
    { 
     return l_(std::forward<A>(args)...); 
    }; 
} 

} 

template <typename F, int I = 0, typename L> 
inline F cify(L&& l) 
{ 
    return cify<F, I>(std::forward<L>(l), F()); 
} 


int main() 
{ 
    int x=0; 
    int y=0; 
    auto f = [x,y](double (func)(int)) -> double 
    {func(0); return 0.0;}; 

    f(cify<double(*)(int i)>([x](int i) -> double //works now 
    {return 0.0;})); 
} 

Click per un esempio di lavoro.